背景
當我知道Optional
也有flatMap
方法的時候我的內(nèi)心是很驚訝的. 之前Collection
的flatMap
就因為兩個重載方法搞得我暈了一陣.而且因為不常用對所以對flatMap
一直沒有一個概念. 最近一些對可選值的使用場景讓我調(diào)研并使用了一下, 結合唐巧的一篇博客, 算是對flatMap
這個關鍵詞有了統(tǒng)一的認識.
使用場景
假設我們要設計一個函數(shù), 函數(shù)參數(shù)是一個image的data, 我們要把它先轉換為UIImage
, 然后將其轉換為jpg
格式,壓縮0.3倍, 然后返回壓縮后的UIImage
. 常規(guī)的話我們可能會這么寫.
func getCompressImage(by data: Data?) -> UIImage? {
guard let data = data,
let image = UIImage(data: data),
let jpegImageData = image.jpegData(compressionQuality: 0.3),
let compressImage = UIImage(data: jpegImageData) else {
return nil
}
return compressImage
}
這樣其實已經(jīng)算是比較清晰了(比起if let)
如果使用flatMap
會是這樣的
func getCompressImage(by data: Data?) -> UIImage? {
return data.flatMap {
UIImage(data: $0)
}.flatMap{
$0.jpegData(compressionQuality: 0.3)
}.flatMap{
UIImage(data: $0)
}
}
flatMap
函數(shù)的核心思想是對一個容器的元素進行再變形,變形為新的容器, 這里的容器就是指Optional
, 所以我們可以看到它可以將異常情況nil
進行下沉. 在一步步的操作中我們并沒有進行解包操作, 只是將有值得情況進行處理, nil
的情況向下傳遞下去. 代碼其實更清晰簡潔了.
并且不是類似的情況都能用guard let
解決, 如果其中一個操作比較復雜,要進行
這樣還不能足夠表現(xiàn)flatMap
的好處. 先看一段傳統(tǒng)寫法的代碼
func getValue(with stringValue: String) -> String {
guard let number = Int(stringValue), number > 100 else {
return "default"
}
let str = String(number + 100)
if str == "1000" {
return str
} else {
return "default"
}
}
為了舉例子寫了一段沒有具體需求的函數(shù), 目的是為了讓它無法簡化多少.
我們看到在兩種為nil
的情況下我們寫了兩次return "default"
但是使用flatMap
func getValue(with stringValue: String) -> String {
return Int(stringValue).flatMap {
return $0 > 100 ? nil : String($0 + 100)
}.flatMap {
$0 == "1000" ? "1000" : nil
} ?? "default"
}
我們將兩種可能為nil
的情況傳遞到函數(shù)調(diào)用的末尾, 統(tǒng)一來返回default
, 并且變化的過程看起來更簡單清晰了
你可以試一下如何優(yōu)化第一種代碼.. 我是不知道它如何能做到一次處理返回default
總結
flatMap
雖然傳的是一個函數(shù), 里面可以做額外的操作, 但是我們最好只做變形操作.不能引發(fā)副作用是函數(shù)式編程的原則..
另外Optional
的這個函數(shù)雖然看起來挺酷, 但是使用不當也會增加閱讀的復雜度,一個guard let
就能解決的話還用個函數(shù)那就有些炫技的嫌疑了.
對于flatMap
的nil下沉我覺得是與其它解包方式最大的區(qū)別. 大家也可以多試試.
flatMap
與map
區(qū)別
在函數(shù)式編程里所有的集合類型都有這兩個方法,
除了集合類型, RxSwift里的Observable
,和標準庫里的Optional
也有這兩個方法, 它們的共性是對某一類型的元素進行了封裝.
我們先將這個封裝了之后的類型叫做容器
, 被封裝的值叫做元素
在集合類型里的元素很好理解, Optional
容器里的元素就是someValue
, Observable
容器的元素就是觀察的值
map
和flatMap
都是容器將元素進行變形得到一個新的容器, 不同的是
map
的變形是 元素
-> 元素
flatMap
的變形是元素
->容器
套用到前面的幾種容器類型就發(fā)現(xiàn)確實如此.... 主要區(qū)別在于transform
函數(shù)的類型.