一、Optional的map和flatMap
Optional 的定義
public enum Optional<Wrapped> : ExpressibleByNilLiteral {
//我們在??寫代碼的時候,習慣用 nil 來表示空值,而不是 .none
case none
//非空的值用 Wrapped 來表示
case some(Wrapped)
//optional只有一個map函數(shù),無?其它變體
public func map<U>(_ transform: (Wrapped) throws -> U) rethrows -> U? {
switch self {
case .some(let y):
return .some(try transform(y))
case .none:
return .none
}
}
//optional只有一個flatMap函數(shù),無?其它變體
public func flatMap<U>(_ transform: (Wrapped) throws -> U?) rethrows -> U?{
switch self {
case .some(let y):
return try transform(y)
case .none:
return .none
}
}
map 和 flatMap的共性
map和flatMap函數(shù)的返回結果依舊是可選性
;但返回的可選值類型可以與原可選值類型不一致。
- 如果?原optional沒有值, map和flatMap函數(shù)都直接?返回nil。
- flatMap?:如果原optional有值?非空,則用解包后的值y做參數(shù)來?調(diào)用閉包transform,閉包的結果?就是flatMap的結果,
transform(y)?
???降維。 - map:如果原optional有值?非空,則用解包后的值y做參數(shù)來?調(diào)用閉包transform,map的返回值是
.some( transform(y) )
例如:
// flatMap降維和 map的對比示例
let a: String? = "1"
let am = a.map{ Int($0) } // 閉包結果類型 Int?
print( am ) // Optional(Optional(1)) map結果類型 Int?? .some( transform(y) )
let afm = a.flatMap{ Int($0) } // 閉包結果類型 Int?
print(afm) // Optional(1) flatMap結果類型 Int?? transform(y)?
// 原來類型:Int?, 返回值類型:String?
let b: Int? = 1
let bm = b.map { String("result = \($0)") } // 閉包結果類型 String
print(bm) /// "Optional("result = 1")"
// 原optional沒有值, map和flatMap函數(shù)都直接?返回nil
let c:Int? = nil
let cm = c.map { String("result = \($0)") } // 閉包結果類型 String
print(bm) /// "nil" map結果類型 String?
結論:那什么時候使用flatMap 方法呢?答案是:當我們的閉包transform(y)的??返回值?是可選型的??時候,可以用f?latMap來降一次維
。
二、Sequence 的? flatMap: 去空降維
每調(diào)用一次flatMap就去一次空、或降一次維
- 去空:把空值nil過濾掉。
- 降維:比如把?二維數(shù)組壓平成一維數(shù)組。
- 每次調(diào)用只能2選一(去空、或降維),?這2個功能各自對應一個不同的重載flatMap函數(shù)。
extension SequenceType {
// 去空:把空值nil過濾。
public func flatMap<T>(transform: (Self.Generator.Element) -> T?) -> [T]
// 降維:比如把二維數(shù)組壓平成一維數(shù)組
public func flatMap<S : SequenceType>(transform: (Self.Generator.Element) -> S) -> [S.Generator.Element]
// [Int?] -> [ Int ] 去空
var a = [1, 3, 5, nil, 7, 9] // a 類型 [Int?]
let afm = a.flatMap{ $0 } // afm 類型是 [ Int ]
print(type(of: afm), ": ", afm) /// [1, 3, 5, 7, 9]
// [ [Int] ] -> [ Int ] 降維
var b = [[1, 3, 5], [7, 9] ] // b 類型 [ [Int] ]
let bfm = b.flatMap{ $0 } // bfm 類型 [ Int ]
print(type(of: bfm),": ", bfm) /// [1, 3, 5, 7, 9 ]
// [ [Int]? ] -> [ [Int] ] 去空
var c = [ [1, 3, 5], [7, 9], nil ] // c 的類型是 [ [Int]? ]
let cfm = c.flatMap{ $0 } // cfm 類型 [ [Int] ]
print(type(of: cfm),": ", cfm) /// [[1, 3, 5], [7, 9] ]
// [ [Int?] ] -> [ Int? ] 降維
var d = [ [1, 3, 5], [7, 9], [nil] ] // c 的類型是 [ [Int?] ]
let dfm = d.flatMap{ $0 } // dfm 類型 [ Int? ]
print(type(of: dfm),": ", dfm) /// [1, 3, 5, 7, 9, nil ]
// [ [Int?]? ] -> [ [Int?] ] 去空
var e = [ [1, 3, 5], [7, 9], [nil], nil ] // c 的類型是 [ [Int?]? ]
let efm = e.flatMap{ $0 } // efm 類型 [ [Int?] ]
print(efm) /// [[1, 3, 5], [7, 9], [nil], [10] ]
結論: 最好在操作 flatMap 時, 最好顯示聲明類型
,這樣可以更清晰的知道應該調(diào)用多少次flatMap,如:
// [ [Int?]? ] -> [ Int ] 一看就知道要調(diào)用3次。
var f: [ [Int?]? ] = [ [1, 3, 5], [7, 9], [nil], nil ] // c 的類型是 [ [Int?]? ]
let ffm3: [ Int ] = f.flatMap{ $0 }.flatMap{ $0 }.flatMap{ $0 } // efm 類型 [ Int ]
print(ffm3) /// [1, 3, 5, 7, 9]