在定义一个泛型函数来体现一个更常见的模式之前,我们会先考虑一些相对简单的函数。定义一个计算数组中所有整型值之和的函数非常简单:
可以像下面一样使用 sum 函数:
我们也可以使用类似 sum 中的 for 循环来定义一个 product 函数,用于计算所有数组项相乘之积:
同样地,我们可能想要连接数组中的所有字符串:
或者说,我们可以选择连接数组中的所有字符串,并插入一个单独的首行,以及在每一项后面追加一个换行符:
这些函数有什么共同点呢?它们都将变量 result 初始化为某个值。随后对输入数组 xs 的每一项进行遍历,最后以某种方式更新结果。为了定义一个可以体现所需类型的泛型函数,我们需要对两份信息进行抽象:赋给 result 变量的初始值,以及用于在每一次循环中更新 result 的 函数 。
考虑到这一点,我们得出了能够匹配此模式的 reduce 函数定义,如下所示:
这个函数的泛型体现在两个方面:对任意 [Element] 类型的 输入数组 来说,它会计算一个类型为 T 的返回值。这么做的前提是,首先需要一个 T 类型的初始值(赋给 result 变量),以及一个用于更新 for 循环中变量值的函数 combine:(T,Element)-> T 。在一些像OCaml和Haskell一样的函数式语言中, reduce 函数被称为 fold 或 fold_left 。
我们可以用 reduce 来定义迄今为止本章出现的所有函数。下面是几个例子:
除了写一个闭包,我们也可以将运算符作为最后一个参数。这使得代码更短,如下面两个函数所示:
要再一次说明,我们自定义 reduce 仅仅只是为了练习。Swift的标准库已经为数组提供了 reduce 函数。
我们可以使用 reduce 来定义新的泛型函数。例如,假设有一个数组,它的每一项都是数组,而我们想将它展开为一个单一数组。可以使用 for 循环编写一个函数:
然而,若使用 reduce 则可以像下面这样编写这个函数:
实际上,我们甚至可以使用reduce重新定义 map 和 filter :
我们能够使用 reduce 来表示所有这些函数,这个事实说明了 reduce 能够通过通用的方法来体现一个相当常见的编程模式:遍历数组并计算结果。
请务必注意:尽管通过 reduce 来定义一切是一个很有趣的练习,但是在实践中这往往不是一个好主意。原因在于,不出意外的话,你的代码最终会在运行期间大量复制生成的数组,换句话说,它不得不反复分配内存、释放内存,以及复制大量内存中的内容。就像我们之前做的一样,用一个可变结果数组定义 map 的效率显然会更高。理论上,编译器可以优化代码,使其速度与可变结果数组的版本一样快,但是Swift 2.0并没有那么做。如果想了解更多详情,请参阅我们的另一本书——《Swift进阶》 [2] 。