队列 Queue Data Structure

实现一个 队列,包括 enqueue、dequeue、peek。 Queue 队列 核心也是 array,A queue gives you a FIFO or first-in, first-out order. 队列是:先进先出的。 public struct Queue<T> { fileprivate var array = [T]() } enqueue 进队,在数组尾部追加元素。 public mutating func enqueue(_ element: T) { array.append(element) } dequeue 出队,将首位的元素移除。因为首位元素移除后,其他元素依次向前移动,所以是 O(n)。 public var isEmpty: Bool { // 使用数组自身的方法,而不是 array.count > 0 return array.isEmpty } public mutating func dequeue() -> T? { // 使用定义的变量 if isEmpty { return nil } else { return array.removeLast() } } peek 查看队首元素。 /// peek() 改为更有语义话的只读变量 public var front: T? { return array.first } 优化出队 在出队后不移动元素而是移动 起始索引,就像动的收银台而不是排队的人。 /// 优化 队列 的出队 public struct OptimizedQueue<T> { /// 这里改为了可选型,为了可以清理无效的元素 fileprivate var array = [T?]() /// 起始索引 fileprivate var head = 0 public var count: Int { // 减去 起始索引 前面的数量 return array.count - head } public var isEmpty: Bool { // 根据实际数量判断 return count == 0 } // 保持不变 public mutating func enqueue(_ element: T) { array.append(element) } public mutating func dequeue() -> T? { guard head < array.count, let element = array[head] else { return nil } // 置空当前位置元素 array[head] = nil // 前移起始索引 head += 1 // 空索引的占用比例 let percentage = Double(head)/Double(array.count) // 50 0.25 都是魔法数字,主要是为了控制数组修剪的频率,可以自行调整 if array.count > 50 && percentage > 0.25 { // 将起始空元素删除 array.removeFirst(head) // 重置 起始索引 head = 0 } return element } public var front: T? { if isEmpty { return nil } else { // 根据 起始索引进行 返回 return array[head] } } } 文章代码:GitHub - imzyf/data-structure-and-algorithm/002-Queue/。 ...

November 22, 2018 · 2 min · 239 words · Me

栈 Stack Data Structure

加入 Swift Algorithm Club /‘ælgə’rɪðəm/,回炉重新学习数据结构与算法。 自己创建的项目:GitHub - imzyf/data-structure-and-algorithm。 实现一个 栈 /stæk/,包含 push peek pop 与 Generics 泛型。 stack 栈 非常像一个数组,它包括少量的方法。 push 添加一个新元素到栈顶 pop 从栈顶移除一个元素 peek 查看栈顶的一个元素但是不 pop A stack gives you a LIFO or last-in first-out order. 栈是后进先出,队列是先进先出。 public struct Stack<Element> { fileprivate var array: [Element] = [] } push push 是在数组的尾部添加元素是以 O(1),如果是在数组最前添加是 O(n) 这是昂贵的。 public mutating func push(_ element: Element) { array.append(element) } 因为使用的 struct,修改属性值的方法要加 mutating。 pop 想从一个空栈中弹出最后一个元素将返回 nil。 ...

November 22, 2018 · 1 min · 126 words · Me

解决 Too many symbol files

在上传 App 到 App Store 后收到邮件,有 issues Too many symbol files。在之前看到 Your delivery was successful,此 issues 不影响发布,所以一直搁置了。 今天决定彻底处理下。 背景 先说 *.symbols 这文件是干嘛的,我现在(2018-10)对此的理解: symbols 为符号表文件 符号表是内存地址与函数名、文件名、行号的映射表 <起始地址> <结束地址> <函数> [<文件名:行号>] 为什么要配置符号表? 为了能快速并准确地定位用户 App 发生 Crash 的代码位置,使用符号表对 App 发生 Crash 的程序 堆栈 进行 解析 和 还原。 项目情况 再说下项目情况,因为数字都是用了的是 Int,为防止 32 位设备发生越界情况(理由好像有点扯),所以项目端设置了设备限制 arm64,也就是 5s 之前的设备不可以安装。 因为使用了三方库,但是三方库是支持 32 位设备的,所以生成了冗余的 symbols 文件。 查询 symbols 文件的生成情况:Xcode Window -> Organizer 选择有问题的 archive,右击选择 Show in finder,命令行进入 *.app 中的 dSYMs 文件夹,执行 dwarfdump --uuid * 可以查询到是否生成了多余的文件。 ...

October 30, 2018 · 1 min · 142 words · Me

在 MySQL 中选择合适的日期类型

如何在 MySQL 中选择合适的日期类型困扰了很久,varchar、int、timestamp、datetime 都有尝试过,近来有所感悟,做此总结。 注:此总结考虑了 PHP 和 Laravel 框架的特点。 使用 varchar varchar 存储日期时间的格式完全可以自己控制,月/日/年 还是 年-月-日 需求怎么说就怎么存,读取后展示是也不用在格式化。同时伏笔也就此埋下:日期时间格式没强制约束,总有一天字段里出现了与众不同的格式;要是日期时间会 变化 或作为 查询条件 或要进行 排序 时就又是一坑,还是要格式化标准格式再处理。可以说 varchar 应该是最差的选择了。 使用 int 与 timestamp PHP time() 可以直接获取当前时间戳秒数,数据库字段要也是 int 一存就完事了,不会有格式问题,谁用什么样转什么样。但是在数据库工具中查看此字段时显示不够直观,范围时会不方便,这些在使用 timestamp 是会得到解决。 timestamp 是我一直迷惑的一个类型。我写了几个例子做测试: 将 Laravel 项目设置为 CST 中国标准时间,MySQL 时区设置为 UTC,使用 now() 获取当前日期时间,比如:2018-5-25 11:00:00 存入 timestamp 类型的字段中,使用数据库工具查看字段结果为仍然为 2018-5-25 11:00:00。 继续上面的操作,项目中使用查询语句查询刚才的记录,结果显示为 2018-5-25 11:00:00,将项目时区从 CST 改为 UTC 后再次查询的结果仍然为 2018-5-25 11:00:00 没有变化。 继续上面的操作,将数据库的时区改为 +8:00,数据库工具、项目查询后的结果为 2018-5-25 19:00:00 发生了变化,修改项目为 CST 查询结果是 2018-5-25 19:00:00 和刚才一样也变化了。 这个测试说明了: ...

May 25, 2018 · 2 min · 254 words · Me

【Modern PHP】笔记

又回到 PHP Web 开发,使用 Laravel 框架,重读《Modern PHP》。 PHP 正在重生。 特性 命名空间 声明命名空间: <?php namespace Oreilly\ModernPHP; 导入和别名: <?php use Symfony\Component\HttpFoundation\Response as Res; $r = new Res('Oops', 400); $r->send(); PHP 5.6 开始可以导入函数和常量: <?php use func Namespace\functionName; use constant Namespace\CONST_NAME; functionName(); echo CONST_NAME; 使用接口 接口是两个 PHP 对象之间的契约,其目的不是让一个对象依赖另一个对象的身份,而是依赖另一个对象的能力。 使用接口编写更加灵活,能委托别人实现细节。 性状 trait 性状是类的部分实现,可以混入一个或者多个现有的 PHP 类中。性状有两个作用:表明类可以做什么(像是接口);提供模块化实现(像是类)。 如果想让两个无关的 PHP 类具有类似的行为,应该怎么呢?性状就是为了解决这种问题而诞生的。性状能把模块化的实现方式注入多个无关的类中。而且性状还能促进代码的重用。 这与创建一个接口,两个无关的类实现这个接口的优势在于:不用写相同的实现代码,符合 DRY 原则。 PHP 解释器在编译时会把性状复制粘贴到类的定义体中,但是不会处理这个操作引入的不兼容问题。如果性状假定类中有特定的属性和方法(在性状中没有定义),要确保相应的类中有对应的属性和方法。 生成器 Generator 是 PHP 5.5.0 引入的功能。生成器是简单的迭代器,仅此而已。 PHP 生成器不要求类实现 Iterator 接口,从而减轻了类的负担。生成器会根据需求计算并产生要迭代的值。这对应该的性能有重大影响。假如标准的 PHP 迭代器经常在内存中执行迭代操作,这要预先计算出数据集,性能低;此时我们可以使用生成器,即时计算并产出后续值,不占用宝贵的内存资源。 PHP 生成器不能满足所有迭代操作的需求,因为如果不查询,生成器永远不知道下一个要迭代的值是什么,在生成器中无法后退和快进。生成器还是一次性,无法多次迭代同一个生成器。不过,如果需要,可以重建或克隆生成器。 PHP 生成器是 PHP 函数,只不过要在函数中一次或者多次使用 yield 关键字。生成器从不返回值,值产出值。 function myGenerator() { yield 'value1'; yield 'value2'; yield 'value3'; } foreach (myGenerator() as $yieldedValue) { echo $yieldedValue, PHP_EOL; } value1 value2 value3 使用生成器处理 CSV: ...

May 8, 2018 · 3 min · 572 words · Me

PhpStorm 使用经验

Getting Started Two shortcuts to get started Shift+Shift(⇧+⇧) helps you find anything within your project. Alt+Enter(Option+Enter) provides instant access to contextual actions and quick fixes relevant to the selected code. Getting started with PHP in PhpStorm 还有个 One Dark theme 但是 Material Theme UI 已经包含这个主题。 配置: Preferences > Appearance & Behavior > Appearance 下,右侧配置:Theme: Darcula,勾选 User custom font: .AppleSystemUIFont Size: 18。 Preferences > Editor > Font 下,右侧配置:Font: Menlo Size: 18 Line spacing: 1.2。 ...

May 5, 2018 · 3 min · 433 words · Me

使用 Certbot 获取 Let’s Encrypt 颁发的 TLS 证书

Certbot 和 Let’s Encrypt 的关系 Let’s Encrypt 一个免费、自动化、开放的公共证书颁发机构(CA)。 通过 ACME(Automatic Certificate Management Environment)协议向域名所有者颁发 DV(Domain Validation)TLS/SSL 证书。 Certbot 由 Electronic Frontier Foundation (EFF) 维护的开源 ACME 客户端。 主要目标是简化与 Let’s Encrypt 之间的交互: 自动化域名验证(HTTP-01、DNS-01、TLS-ALPN-01 等) 安装并续期证书 更新 Web 服务器配置(Apache、Nginx、Lighttpd 等) Certbot 也能与任何兼容 ACME 的 CA 通信,不限于 Let’s Encrypt。 典型工作流程 解析参数并检测服务器类型。 选择并执行挑战(例如在 /.well-known/acme-challenge/ 下写入 token)。 Let’s Encrypt 回访验证域名归属。 验证通过后签发证书;Certbot 下载并安装到本地。 创建定时任务 certbot renew 自动续期。 开始实验 实验环境:Amazon Linux 2023 (AL2023) 知识补充 dnf(Dandified YUM) 是 RPM-系 Linux 发行版的下一代包管理器。它在功能上取代了传统的 yum,两者命令参数几乎保持兼容。 RPM 一开始叫 Red Hat Package Manager,后来改名为递归含义的 RPM Package Manager。 RPM-系发行版 是把 RPM 作为原生软件包格式与核心包管理工具链的那一族 Linux 发行版。常听到的 Fedora / RHEL / CentOS / AlmaLinux / Rocky Linux / openSUSE / SUSE Linux Enterprise / Amazon Linux 2023 等。 dnf 不是对 rpm CLI 的简单封装,而是调用 librpm 完成最终操作。 包管理器 代表发行版 归属 dnf / yum Fedora, RHEL, CentOS RPM zypper openSUSE, SLE RPM apt / apt-get Debian, Ubuntu DEB pacman Arch Linux tar.xz apk Alpine .apk snap 是一种由 Canonical(Ubuntu 的开发公司)推出的跨发行版应用打包和分发格式,也指围绕它的一整套生态系统。 跨发行版。同一个 snap 包可以在几十种发行版上直接安装运行,不依赖各自的 RPM/Deb 系统仓库。 自包含(bundled)依赖。snap 包内部包含所有依赖,无需外部安装。 https://snapcraft.io/ Launch an AWS EC2 instance Instance type: t2.nano Username: ec2-user Security Groups: Allow HTTP and HTTPS Public IPv4: 35.86.90.4 配置 nginx sudo dnf update -y sudo dnf install -y nginx nginx -v # nginx version: nginx/1.28.0 sudo systemctl start nginx sudo systemctl enable nginx systemctl status nginx # Started nginx.service - The nginx HTTP and reverse proxy server. 直接通过 http://35.86.90.4,会看到默认的 nginx 欢迎页面。 ...

April 26, 2018 · 5 min · 914 words · Me, LLM

Swift 初始化

因为自己是直接从 Swift 进入的 iOS 开发,Swift 与 Objective-C 初始化的对比就不多提了。感觉上 Swift 初始化的方式像 Java,自己也只这样套着 Java 去理解,但也发现了不相同的地方。 初始化顺序 class Blog: NSObject { let param: String override init() { } } 这里有条错误 error: property 'self.param' not initialized at implicitly generated super.init call 说明:param 参数没有在隐式生成 super.init 调用之前完成初始化。 Swift 中并不是不调用 super.init 而是为了方便开发者由编译器完成了这一步,但是要求调用 super.init 之前要完成成员变量的初始化。 class Blog: NSObject { let param: String override init() { param = "swift init" } } 对于需要修改父类中成员变量值,我们需要在调用 super.init 之后再进行修改: class Cat { var name: String init() { name = "cat" } } class Tiger: Cat { let power: Int override init() { power = 10 super.init() name = "tiger" } } Swift 中类的初始化顺序: ...

March 31, 2018 · 5 min · 972 words · Me

iOS Safe Area 我所知道的全部

在 iOS 7 Apple 在 UIViewController 中引入了 topLayoutGuide 和 bottomLayoutGuide 属性来描述没有被覆盖(status bar, navigation bar, toolbar, tab bar, etc.)屏幕的区域。在 iOS 11 中,Apple 已经弃用了这些属性,并引入了 safe area。Apple 建议我们不要在 safe area 操作,在 iOS 11 中,当在 iOS App 中定位视图时,你必须使用新的 safe area API。 UIView 在 iOS 11 UIViewController topLayoutGuide 和 bottomLayoutGuide 属性已经被替换成了新的 UIView 中的 safe area: @available(iOS 11.0, *) open var safeAreaInsets: UIEdgeInsets { get } @available(iOS 11.0, *) open var safeAreaLayoutGuide: UILayoutGuide { get } safeAreaInsets 属性意味着屏幕可以覆盖从四个方向,而不仅仅是顶部和底部。当被 iPhone X 呈现时,我们就明白了为什么我们需要左右 insets。 iPhone 8 vs iPhone X safe area (portrait orientation) ...

March 29, 2018 · 5 min · 965 words · Me

UITableViewCell 自适应 UITextView 高度

使用 Auto Layout 让 UITableViewCell 自适应 UITextView 高度,效果演示: 99-projects-of-swift/029-tableviewcell-self-adaption 预备步骤 给 textView 上下左右建立相对于 cell 的约束 取消 textView 的 Scrolling Enabled 设置 tableView 估算高度 tableView.estimatedRowHeight = 70 设置 textView.delegate = self 关键点 如果在 textViewDidChange(textView:) 调用 tableView.reloadData() 会造成 textView 失去焦点,键盘隐藏。 解决方法: func textViewDidChange(textView: UITextView) { tableView.beginUpdates() tableView.endUpdates() } 这里带来了一个问题,当 textView 长度超过一屏或者过长时,在输入时 tableView 会跳动滚动 jumping and stuttering。 更好的解决方法: func textViewDidChange(textView: UITextView) { let currentOffset = tableView.contentOffset UIView.setAnimationsEnabled(false) tableView.beginUpdates() tableView.endUpdates() UIView.setAnimationsEnabled(true) tableView.setContentOffset(currentOffset, animated: false) } 禁用动画和重建表视图的内容偏移修正抖动。 ...

March 27, 2018 · 1 min · 87 words · Me