Swift 的语法很适合写函数式程序。函数式“三板斧” map、filter、reduce 都应该很熟悉了。另外 flatMap 和 zip 也很常用。
flatMap
flatMap 一个常见用法是在 map 的基础上,剔除掉空值。比如下面代码
1 | var images = [UIImage]() |
就可以改写成
1 | let images = (1 ..< 10).flatMap { i in |
flapMap 另一个常见的用法是插入额外元素。比如
1 | let a = [1, 2, 3, 4, 5, 6] |
zip
zip 是将两个序列的元素,一一对应合并成元组,生成一个新序列。比如
1 | let a = [1, 2, 3, 4] |
生成的序列,如同原始两个序列的相互咬合,因此函数的名字为 zip。zip 的英文有拉链的意思。生成的序列 count 为原始序列的最小值。
zip 生成的序列通常会进行下一步处理。比如
1 | func loadColors(colors: [UIColor]) { |
上面这段的语句,为颜色按钮分别赋予颜色值。相当于:
1 | func loadColors(colors: [UIColor]) { |
再举一段代码。
1 | let colors = [UIColor.red, UIColor.blue, UIColor.white] |
这段代码,创建了颜色按钮,并用索引设置了对应的 tag。
最后
这些简单的函数,配合起来可以达到一些高级的功能。比如:
1 | let a = ["a", "b", "c", "d"] |
这里将两个序列的元素,间隔地插入,合并成一个序列。
第一次看这种代码,可能会看不懂。有些人可能会觉得太复杂了,为什么不直接写循环呢,这样每个人都可以看懂。其实这种风格的程序反而是简单的。按照我理解,简单并不一定是直观的,简单更多意味着统一。有些写法并不一定第一次就能看懂,可能需要经过一定努力才能学会,但一旦学会了,就可以无差别地统一处理一大批问题。既直观又统一当然最好,但两者不可兼得,我会优先取统一而非直观,这样反而更简单些,不用处理额外情况。
比如 zip 函数,当两个序列的 count 不相等时,会取 count 的最小值。这种两个序列 count 不相等,就属于额外情况。当自己手写循环时,很可能就忘记处理了。
另外时不时就遇到 Swift 一些诡异的 bugs。比如这段代码就编译不过:
1 | let a = [UIView(), UIView(), UIView()] |
而这样写就可以编译通过:
1 | let a = [UIView(), UIView(), UIView()] |
遇到这些 bugs, 就需要手动添加变量类型。