Swift 2.0 :揭秘 Map 和 FlatMap
Swift 依然是一个有些不稳定的语言,每次发布新版本,都带来新的功能和特性。许多人都已经写了 Swift 的函数的相关内容以及如何用更“纯”的函数式的方法处理问题。 <center> 考虑到 Swift 语言依然在初期状态,往往在尝试去理解一些特定的话题时,最后你会发现许多文章都是用 Swift 2.0 之前的语法,或者更糟糕的一些,内容混杂着多个版本语法。有时,搜索 相关概念缺乏全面介绍的新文章,现在的许多文章常常用一些不是通俗易懂的例子和生硬的隐喻,一些人甚至使用一些难以理解的方式思考问题。 在这篇简短的文章中(这是 Swift与函数式系列文章中的一篇),我将通过引用当前库的头文件,试着对【如何在 Swift 2.0 中对不同的类型使用 目录
Mapmap 和 flatmap 方法中, map 有着更清晰的行为,它简单的对输入执行一个闭包,和 flatMap 一样,它可以用在 Optionals 和 SequenceTypes 上(如:数组、词典等)。 在 Optionals 上使用 Map下面是 Optionals 上 map 方法的原型: public enum Optional<Wrapped> : ... { ... /* 如果 `self == nil` ,直接返回 `nil` 。否则返回 `f(self!)` 。 */ public func map<U>(f: (Wrapped) throws -> U) rethrows -> U? ... } 这个 map 方法期望一个签名为 注意到输出类型可以和输入类型不同,这就带来了大量有用的特性。 老实说,这里不需要多余的解释了,让我们直接看这篇文章 Playground 上的代码吧: var o1:Int? = nil var o1m = o1.map({$0 * 2}) o1m /* Int? 类型,结果为 nil */ o1 = 1 o1m = o1.map({$0 * 2}) o1m /* Int? 类型,结果为 2 */ var os1m = o1.map({ (value) -> String in String(value * 2) }) os1m /* String? 类型,结果为 2 */ os1m = o1.map({ (value) -> String in String(value * 2) }).map({"number "+$0}) os1m /* String? 类型,结果为 "number 2" */ 如果我们总是需要修改原始的可选值,使用 map 就可以保留原始的值,(map 只是在可选值有值的时候才执行这个闭包,否则就只是返回 nil)。但最令人兴奋的特性是我们可以自由的连接多个 map 操作,他们会有序的执行,这多亏了调用 map 总是会返回一个可选值。这样,我们就能够进行可选值的链式调用了。 在 SequenceTypes 上使用 Map但是在 SequenceTypes 上,比如数组和字典,使用 map 方法就很难跳过为空的可选值: var a1 = [1,2,3,4,5,6] var a1m = a1.map({$0 * 2}) a1m /* [Int] 类型,结果为 [2,6,8,10,12] */ let ao1:[Int?] = [1,6] var ao1m = ao1.map({$0! * 2}) ao1m /* [Int] 类型,结果为 [2,12] */ var a1ms = a1.map({ (value) -> String in String(value * 2) }).map { (stringValue) -> Int? in Int(stringValue) } a1ms /* [Int?] 类型,结果为 [.Some(2),.Some(4),.Some(6),.Some(8),.Some(10),.Some(12)] */ 这时我们调用的 map 方法在 SequenceTypes 下定义成这个样子: /* 返回一个对 `self` 每个元素进行变换后的结果数组
这个变换的闭包类型 这些基本就是你可以用 var s1:String? = "1" var i1 = s1.map { Int($0) } i1 /* Int?? 类型,结果为 1 */ var ar1 = ["1","2","3","a"] var ar1m = ar1.map { Int($0) } ar1m /* [Int?] 类型,结果为 [.Some(1),.Some(2),.Some(3),nil] */ ar1m = ar1.map { Int($0) } .filter({$0 != nil}) .map {$0! * 2} ar1m /* [Int?] 类型,结果为 [.Some(2),.Some(6)] */ 并不是每个 String 都可以转成 Int ,所以我们的整数转换闭包总是返回一个 Int? 类型 。那在第一个例子中发生了什么?为什么返回的是 Int?? ,也就是结尾为什么是一个可选值的可选值,在执行 map 后多了一个可选包裹。解包两次才可以得到真正包含的值,虽然不是什么大问题。但当我们需要链式添加 map 操作符时就会显得很麻烦。我们即将看到, 在这个数组的例子中,如果一个 String 不能转换成 Int ,就像 好了,我们只需要在中间过滤出可用的元素,并且为下一个 map 操作准备好数据流。把这些行为嵌入到一个 FlatMap
虽然 在 Optionals 上使用 FlatMap这个方法的定义有一些不同,但功能是相似的,只是改写了一下注释的含义: public enum Optional<Wrapped> : ... { ... /* 如果 `self` 是 nil ,直接返回 `nil` ,否则返回 `f(self!)` 。 */ public func flatMap<U>(f: (Wrapped) throws -> U?) rethrows -> U? ... } 就闭包而言,这里有一个明显的不同,这次 对于可选值, flatMap 对于输入一个可选值时应用闭包返回一个可选值,之后这个结果会被压平,也就是返回一个解包后的结果。 本质上,相比 var fo1:Int? = nil var fo1m = fo1.flatMap({$0 * 2}) fo1m /* Int? 类型,结果是 nil */ fo1 = 1 fo1m = fo1.flatMap({$0 * 2}) fo1m /* Int? 类型,结果是 2 */ var fos1m = fo1.flatMap({ (value) -> String? in String(value * 2) }) fos1m /* String? 类型,结果是 "2" */ var fs1:String? = "1" var fi1 = fs1.flatMap { Int($0) } fi1 /* Int? 类型,结果是 1 */ var fi2 = fs1.flatMap { Int($0) }.map {$0*2} fi2 /* Int? 类型,结果是 2 */ 最后一段代码包含了一个链式调用的例子,使用 接下来我们再来看一看在 SequenceType 下的操作,这是一个将结果压平的步骤。
这个行为附带着单子(Monad)上的 在 SequenceType 上使用 FlatMapSequenceType 提供了下面默认的 /// 返回一个将变换结果连接起来的数组 /// /// s.flatMap(transform) /// /// 等价于 /// /// Array(s.map(transform).flatten()) /// /// - 复杂度: O(*M* + *N*),这里的 *M* 是指 `self` 的长度 /// *N* 是变换结果的长度 func flatMap<S : SequenceType>(transform: (Self.Generator.Element) throws -> S) rethrows -> [S.Generator.Element] /// 返回一个包含非空值的映射变换结果 /// /// - 复杂度: O(*M* + *N*),这里的 *M* 是指 `self` 的长度 /// *N* 是变换结果的长度 func flatMap<T>(transform: (Self.Generator.Element) throws -> T?) rethrows -> [T]
这两个注释的闭包描述了两个 我们来看看是什么意思: var fa1 = [1,6] var fa1m = fa1.flatMap({$0 * 2}) fa1m /*[Int] 类型,结果是 [2,12] */ var fao1:[Int?] = [1,nil,6] var fao1m = fao1.flatMap({$0}) fao1m /*[Int] 类型,结果是 [1,6] */ var fa2 = [[1,2],[3],[4,6]] var fa2m = fa2.flatMap({$0}) fa2m /*[Int] 类型,结果是 [1,6] */ 虽然第一个例子和之前使用 实际上,有许多使用 对于上一个部分最后的代码片段的一个例子,我们现在可以使用 var far1 = ["1","a"] var far1m = far1.flatMap { Int($0) } far1m /* [Int] 类型,结果是 [1,3] */ far1m = far1.flatMap { Int($0) } .map {$0 * 2} far1m /* [Int] 类型,结果是 [2,6] */ 在这个场景看起来只是一点点的改进,但随着更长的链式,使用 让我重申一遍,也是在这个情况下, Swift 中的 flatMap 行为与 Monads 的 在这个系列的下篇文章 (SwiftGG 译文)你可以学到更多关于 SequenceType 和 GeneratorType 协议的知识。
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |