Swift 的函数式编程
面向对象是我们一直以来思考世界, 思考代码的方式.1
2
3
4[receiver do: this
with: that];
// 实际上编译后是通过这个发送消息
objc_msgSend()
一般我们看到一个函数, 比如hello world
,1
2
3func hello() {
print("hello world")
}
但是他并不符合函数的映射关系, 你可以一对一, 多对一, 但是这样没有输入输出的实在是 no make sense,func hello(name: String)
这样有输入比上面好, 但是没有输出, 仍然算不上是一个函数.
1 | func hello(name: String) -> String { |
这样就好很多, 有输入, 有输出, 同一个输入, 只会有同一种输出. 这也是一种没有副作用的函数
可测试性, 可重复性
有时候, 会在我们没有注意到的地方, 我们修改了其中的某些状态. 我们不想这么做, 因为装有状态的改变的话, 我们将可以对这个函数测试, 也可以重复. 因为状态的改变有可能引起副作用, 同一个输入, 有不同的输出了.
比如, 有一辆车, 你告诉他往前移动一段距离, 再往前移动相同的距离. 这时候我们有两次相同的输入, 但是的到了不同的输出.
这就很难进行测试.
我们能将这个返回输出为一个更新位置的车. 这样我们能向同一辆车发出往前5米的指示, 这样返回的车一直都是这辆车往前5米的车, 但是这样的话, 和我们的逻辑有很大出入, 往前了5米, 然后获得了一辆新的车??
我们换一种逻辑来表示这个事情, 我们说有个 CarView 是显示绘制这类车的, 就是说, 在某个位置绘制出一辆车.
1 | class CarView: UIView { |
我们将车往前移动一段距离, 然后返回一个新的距离的 Carview, 我们在新的位置 draw 一个 car, 这样我们就没有改变任何一个内部状态.
然后我们就 Keyi 进行测试, 有同一个输入, 就有一样的输出.
我们不需要Monads
Swift 支持了 面向对象和面向函数2种变成范式, 我们不必要使用 Monads(来自 Haskell scala 以及其他函数式变成语言的概念, 为了在函数式做使用可变性的一些东西), Swift 中有对象, 有 let
和 var
, 它并不是一个纯函数的语言. 所以我们不会收到这种函数式的限制
使用函数来将原来的数据类型映射为你需要的类型
通过函数, 使得我们平时在进行类型转换的时候得到更方便的调用方法, 而且具备更大的课测试性.
1 | extension Array where Element == String { |
这里有一个将一个字符串变换成另一个字符串的函数 A, 映射的思路是: 如果我给定一个字符串数组, 然后我想提升(lift) A 这个函数, 之前是处理一个字符串, 现在可以给他一个字符串数组, 然后它能返回另一个数组.
在extension
中我们用了 where Element == String
, 限定了这个扩展只用于字符串数组. 如果我们有一个 A 函数, 那么可以传入这个 A, 把它提升为另一个函数: 接收一个字符串数组, 返回一个处理过的字符串数组.
这里将的函数的作用, 就是映射和提升的作用. 我们可以从一个点, 不断的映射和提升, 达到另一个点或者其他的某个方面, 当重复多了, 讲多了, 就会明白映射的机制.
映射和 optional
嗯…这里不知道是翻译有点乱的原因还是本人水平有点低的原因, 看的有点乱…
总之意思就是.. 将一个 func xx(String -> String)
转换为 func oo(String? -> String?)
, 然后假设有个函数能将某个类型映射为另一个类型. 比如 String -> Int
可视化工具(不知道是什么鬼)
假设有这么个可视化工具(想像不出的, 可以参考我的想像…逃…我想的是这样的: 输入)->输出
), 然后就可以把各种关系通过像集成电路一样搭建起来, 只要让参数按照正确的顺序通过即可(类似: A)->B)->C)->D
). 就像一条管道一样, 我们需要一个String
映射到Int
的函数, 因此我们可以将任何一种类似的函数放到里面. 现在就的到了一条管道, 一直流到你想要转换的类型.
可空值, Optional
1 | extension Optional where Wrapped == String { |
因此现在有了映射和可空值之后,就像我们对数组进行扩展一样,我们对可空值进行扩展,现在我让这个从字符串映射为另一个字符串的函数,也就是这里这个我传入的函数,我现在将让其生成一个将可空字符串转换为另一个可空字符串的函数,就像之前我们对数组所做的那样。
实现属于你自己的映射
如果要深刻理解这些概念的话,那么就需要自行来实现映射的功能。当你碰到这样一种情况,你可能会想到:“哦,有什么能帮我解决这个问题吗?就是你了,映射!”所以,我会给大家简要展示一个例子,大家可以尝试进行练习,但是这种练习非常值得,因为 Swift 标准库并没有为大家提供这个玩意儿,也就是 Result 类型。
1 | enum Result<Value, Error> { |
在 Swift
中,Result
类型出现的时间比 Error
要早,现在我们仍然有很多人在使用它,Result
类型的这两个参数皆为泛型。它需要一个 Value
类型或者 Error
类型,然后如果成功的话,那么就会将结果值封装在 .success
枚举值当中返回出来;如果失败的话,那么我就会将错误信息封装在 .failure
枚举值中返回。 这非常类似于可空值,其中 .failure
对应于 .none
,.success
对应于 .some
。因此,对吧!如果我编写一个能将 Result
映射为另一个 Result
的函数,那么我就能得到 ResultA.error 映射为 ResultB.error 的玩意儿。 这两者均为 Error
类型,就像之前两者均为 nil
类型一样,但是不同之处在于,我要将 A 到 B 的映射,提升为 ResultA.error
到 ResultB.error
的映射。 让我们对 Result
进行一下扩展。由于 Result
的 Value
和 Error
均为泛型,所以我的这个 A
到 B
的函数,将变成 Value
到 TargetValue
的函数,最后我就可以得到有 TargetValue
和 Error
类型的 Result
对象。
1 | extension Result { |
后面的就是多举几个例子了…