《函数式 Swift》读书笔记
函数在 Swift 中是一等公民,换句话说,函数可以作为参数被传递到其他函数,
也可以作为其他函数的返回值。函数式编程的核心理念就是函数是值。
函数式思想
在 Swift 中计算和传递函数的方式与整形或布尔型没有任何不同。
假设有一个 struct 包含 x 和 y 坐标:
1 | struct Position { |
此时,定义一个 typealias,表示把 Position 转化为 bool 的函数。typealias Region = (Position) -> Bool
。
接下来创建函数的返回值就可以是一个函数(Region)
1 | func circle(radius: Int) -> Region { |
其中
1 | { (参数) -> (返回值) in |
大括号内就是表示一个函数。
案例研究:封装 Core Image
书中的用例是对 CIImage 进行封装。为了让自己更明白本章所讲的内容,我把 CIImage 换成了 Int 类型来实现,两者的本质并无太大的区别,重点是对函数化这一概念要有清晰的认识。
我们首先将 Filter 定义为一个函数,该函数接受 Int 型作为参数,并且返回一个新的 Int 型。typealias Filter = (Int) -> Int
。
首先,我们定义一个简单的 +1 的方法,返回值是 Filter(是一个函数)。
1 | func addOne(describe: String) -> Filter { |
这样调用起来就是: addOne(describe: "简单的 10+1")(10) // 11
接下来在的定义一个 +2 的方法,同上
1 | func addTwo(describe: String) -> Filter { |
到这里都挺简单的,现在如果我们要再实现一个 +3 的方法,再复制就显得太蠢了,所以转换一下思路,通过 +1 和 +2 的方法实现:
1 | func addThree(describe: String) -> Filter { |
复合函数
现在,如果我们想要实现 0 + 1 + 2 + 3 的功能,就可以这么调用:addThree(describe: "3")(addTwo(describe: "2")(addOne(describe: "1")(0))) // 6
。
感觉这么调用并不友好,我们需要优化一下实现方式。书中给了一种方案是使用复合函数。
首先定义一个用于组合的函数:
1 | func compose(filter1: @escaping Filter, fileter2: @escaping Filter) -> Filter { |
这样可以使用复合函数来定义:compose(compose(addOne(describe: "1"), addTwo(describe: "2")), addThree(describe: "3"))(100) // 106
自定义运算符
但是看起来也很不容易理解。为了让代码更具可读性,我们可以再进一步,为组合引入运算符。
1 | infix operator >>>: pro |
这样就可以用运算符来实现了,
1 | let result = addOne(describe: "1") >>> addTwo(describe: "2") >>> addThree(describe: "3") |
柯里化
函数接受参数之后,返回一个闭包,然后等待第二个参数。add(1)(2)
。函数中的箭头 -> 向右结合。这也就是说没你可以将 A -> B -> C 理解为 A -> (B -> C)。
Map、Filter 和 Reduce
Map 和 Filter
关于 Map 官网的描述是:
Returns an array containing the results of mapping the given closure over the sequence’s elements.
翻译过来就是: 返回一个数组,数组中的元素是由闭包提供的映射序列产生的。
func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]
其中关于参数的描述是:
A mapping closure. transform accepts an element of this sequence as its parameter and returns a transformed value of the same or of a different type.
一个映射的闭包,以一个序列的每个元素作为参数,返回一个相同或不同的类型新值。可以知道我们需要提供的参数是一个闭包。这个闭包中的参数和返回值可以是任意类型的(由泛型定义)。
Map的定义是这样的:
1 | extension Array { |
所以在调用的时候,我们就需要提供一个变换方法的闭包。
1 | array.map { $0.lowercaseString } // ["vivien", "marlon", "kim"] |
Reduce
关于 Reduce 官网的描述是:
Returns the result of combining the elements of the sequence using the given closure.
翻译过来是:返回组合运算的结果,组合运算是由提供的序列经过闭包处理每个元素产生的。
func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
泛型和 Any 类型
除了泛型,Swift 还支持 Any 类型,它能代表任何类型的值。泛型可以用于定义灵活的函数,类型检查仍然由编译器负责;而 Any 类型则可以避开 Swift 的类型系统(所以应该尽可能避免使用)。
泛型函数的类型十分丰富,这里我们可以考虑一下上一章对自定义函数组合运算符 >>> 的泛型实现:
1 | infix operator >>|: pro |
可选值
??
运算符的简易定义是这样的:
1 | infix operator ?? |
??
运算符提供了一个相比于强制可选解包更安全的替代,并且不像可选绑定一样繁琐。