是的,这真的只是入门,一点点了解而已,真正的函数式编程还差得远呢。只是初步学习了一些入门的思想,争取为以后的开发中提供一些便利。
开始
首先记一个 Swift 函数中的变化,其实也不能说是变化,因为在 Swift 3 就已经是这样了,就是函数中的参数已经不能使用 var 了,也就是函数参数全都是常量,如果希望在函数内部改变,那么只有在函数内部重新声明一个变量去接收这个参数。
1 | func test(number: Int) -> Int { |
我们如果想直接操作实参呢?更加复杂,需要增加 inout 关键字。
1 | func addOne(number: inout Int) { |
函数式编程更希望函数像是一个黑匣子一样,参数传进去,黑匣子经过一定逻辑然后返回一个结果,而并不希望我们直接操作这个参数,因此 Swift 函数参数默认都是值传递。
那么我们希望交换两个数呢?这时候肯定还是需要使用 inout 的嘛,我们定义这样一个函数。
1 | func swapTwoInts( _ a: inout Int,_ b: inout Int) { |
当然事实上,Swift 给我们提供了 swap 函数,并且是支持泛型的,也就可以交换两个字符串,两个对象,两个很多东西。我们来自己实现一下:
1 | func swapTwo<T>(_ a: inout T, _ b: inout T) { |
到这里还都是函数的基本使用,没有多少函数式编程的东西啊。
函数式编程入门
Swift 为什么说它支持函数式编程呢,其中一点就是它把函数列为了“一等公民”,函数可以作为变量,可以作为参数,可以作为返回值,这为函数式编程打下了基础。
函数作为变量/常量
1 | func add(num1 a: Int, num2 b: Int) -> Int { |
我们把一个函数名直接赋值给了一个常量,此时 add2 的类型为 (Int, Int) -> Int
,也就是说这个变量是一个函数类型的,之后我们使用 add2(3, 5)
的方式依然可以调用这个函数(只不过没有了参数名)。
函数作为参数
我们都熟悉 Objective-C 中的 Block,而 Swift 中的 Closure 其实和 Block 是一回事儿,都是匿名函数。Swift 由于函数可以作为变量,我们原来传入闭包的地方现在传入一个函数变量是完全没问题的。
sort 函数
比如 Swift 中的 sort 函数(sorted() 有返回值,不改变自身,sort() 返回类型为 Void,改变自身;事实上 Swift 中很多类似方法都是这样的,加后缀的一般都是有返回值不改变自身的)。
1 | var numbers = [4, 7, 2, 1, 7, 12, 3] |
上面就是函数作为参数的一个例子,这里的 sort 函数称为高阶函数。
我们能传入函数的地方也能传一个闭包。
1 | numbers.sort { (num1, num2) -> Bool in |
当然,sort 函数能实现的功能可不知这些,比如下面,我们可以把一个整型数组按照字符串的顺序排序:
1 | var numbers = [123, 110, 1, 23, 235, 20, 315] |
下面介绍所有支持函数式编程的语言都基本会有的三个函数,那就是著名的 map, filter 和 reduce 函数。
map 函数
map 函数作用在数组上,我们传入一个函数,告诉这个数组每一个元素应该按照什么样的逻辑发生改变,那么 map 操作就会把数组中的所有元素均按照这个逻辑去改变,并返回一个新的数组。
1 | numbers.map { $0 + 1 } // 数组所有元素均加 1 |
filter 函数
filter 函数,我们传入一个返回值为 Bool 的函数,所有返回值为 true 的元素会被放进一个新的数组中。
1 | numbers.filter { $0 > 5} // 将所有大于 5 的数挑选出来组成一个新的数组 |
reduce 函数
reduce 函数是把数组中所有的元组组成一个值,比如数组中所有元素的和,所有元素的积,都可以使用。
1 | var numbers = [4, 7, 2, 1, 7, 12, 3] |
柯里化
王巍的《Swifter - Swift 必备 Tips》第一篇就是讲的柯里化。
1 | func addOne(_ num: Int) -> Int { |
上面的函数把一个整型加 1,可是如果我们还需要一个加 2 的函数呢,再写一个?如果需要加 3 的函数呢?再从头到尾写一个?
事实上我们可以定义一个高阶函数,把这几个函数的相同部分抽取出来,如下面:
1 | func addTo(_ adder: Int) -> (Int) -> Int { |
柯里化其实就是把一个多参数的函数,通过抽取高阶函数,把参数的传入分层的过程。我们利用它可以做什么?如果我们有多个函数,它们拥有部分相同的逻辑,我们就可以考虑把这部分逻辑抽取出来,达到代码复用的目的。
函数作为返回值
这里使用慕课网中刘宇波老师的例子:
1 | // 按照重量的1倍计算邮费 |