文章目录
  1. 1. 原文代码
  2. 2. 代码段说明
  3. 3. 其他

原文地址:Functional Snippet #4: Flattening and Mapping Arrays
原文日期:2014-10-27

原文代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
infix operator >>= {}
func >>=<A, B>(xs: [A], f: A -> [B]) -> [B] {
return xs.map(f).reduce([], combine: +)
}
let ranks = ["A", "K", "Q", "J", "10",
"9", "8", "7", "6", "5",
"4", "3", "2"]
let suits = ["♠", "♥", "♦", "♣"]
let allCards = ranks >>= { rank in
suits >>= { suit in [(rank, suit)] }
}
// Prints: [(A, ♠), (A, ♥), (A, ♦),
// (A, ♣), (K, ♠), ...

代码段说明

上面代码涉及到的知识点比较多,最近翻译了一些函数式编程的文章,上面的代码都涉及到了这些文章的知识点。

  • 运算符重载

苹果官方教程对运算符重载有比较细致的介绍,这里就不重复了。这里主要上面代码中的运算符的声明进行说明:

定义了一个中位运算符(infix)>>=, 使用默认的优先级(默认为100,加法是140,优先级越高,越先计算,不过前提是需要有相同的结合性)和结合性(默认是none,表明不能结合。相同的优先级前提下,left的结合性表示先从左边计算,right的结合性从右边计算。)

  • 泛型

运算符定义时,用到了泛型。使用了A,B 2个不同的类型参数(type parameter), 并且没有对类型参数进行限制。从定义可以看出,运算符的作用是,把数组[A],通过函数f的作用,变为数组[B],其中函数f输入一个A,得到一个只有一个元素的数组B。因为参数f是放在参数列表的最后,所以可以使用尾部闭包的语法。

  • map和reduce
    运算符实现时,用到数组的map和reduce方法。首先使用map通过函数f把数组xs中的每个元素转换成只有一个元素的数组[B],然后使用reduce把这些一个元素的数组连接起来,得到一个与xs大小相同的数组。
  • 调用
    代码中最后的调用使用了嵌套的方式,输出了扑克牌中所有的组合。

其他

上面的运算符实现很像数组map方法,输入一个数组,得到另外一个数组,那么以下代码是否跟上面的结果相同呢?大家可以试试。

1
2
3
4
let allCards1 = ranks.map{ rank in
suits.map { suit in (rank, suit)}
}
allCards1

刚开始我还以为是相同的,可以运行了发现,完全不一样。allCard1得不到我们想要的东西。
map方式得到的数组大小是13,每个元素是一个子数组,包含4个不同suit,相同rank的tuple。
原因也比较简单,因为运算符的实现,通过了reduce进行了降纬操作。即使是嵌套,也只是生成一个元素大小为1的数组,然后使用+进行连接,得到最终结果。而使用map的嵌套,没办法进行降纬,上例中,内层的suits.map首先生成了包含4个元素的数组,其中rank待定,suit是分别是4个类型。外层的ranks.map,传入待定的rank,得到一个大小为13的数组(因为map得到的数组大小必须跟原来数组大小保持一致)。

完整代码放在Github

参考资料:

文章目录
  1. 1. 原文代码
  2. 2. 代码段说明
  3. 3. 其他