Swift高階函數(shù):Map伦腐,F(xiàn)latMap赢底,F(xiàn)ilter,Reduce指南和實踐

一柏蘑、Map , FlatMap , Filter , Reduce 指南

Swift是支持一門函數(shù)式編程的語言幸冻,擁有MapFlatMap,Filter,Reduce針對集合類型的操作咳焚。在使用Objective-C開發(fā)時洽损,如果你沒接觸過函數(shù)式編程,那你可能沒聽說過這些名詞革半,希望此篇文章可以幫助你了解Swift中的Map碑定,FlatMap,Filter,Reduce流码。

Map

首先我們來看一下mapSwift中的的定義,我們看到它可以用在 OptionalsSequenceType 上(如:數(shù)組延刘、詞典等)漫试。

public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {
    /// If `self == nil`, returns `nil`.  Otherwise, returns `f(self!)`.
    @warn_unused_result
    public func map<U>(@noescape f: (Wrapped) throws -> U) rethrows -> U?
}

extension CollectionType {
    /// Returns an `Array` containing the results of mapping `transform`
    /// over `self`.
    ///
    /// - Complexity: O(N).
    @warn_unused_result
    public func map<T>(@noescape transform: (Self.Generator.Element) throws -> T) rethrows -> [T]
}

@warn_unused_result:表示如果沒有檢查或者使用該方法的返回值,編譯器就會報警告碘赖。
@noescape:表示transform這個閉包是非逃逸閉包驾荣,它只能在當前函數(shù)map中執(zhí)行,不能脫離當前函數(shù)執(zhí)行普泡。這使得編譯器可以明確的知道運行時的上下文環(huán)境(因此播掷,在非逃逸閉包中可以不用寫self),進而進行一些優(yōu)化撼班。

Optionals進行map操作

簡要的說就是歧匈,如果這個可選值有值,那就解包权烧,調(diào)用這個函數(shù)眯亦,之后返回一個可選值,需要注意的是般码,返回的可選值類型可以與原可選值類型不一致:

///原來類型: Int?,返回值類型:String?
var value:Int? = 1
var result = value.map { String("result = \($0)") }
/// "Optional("result = 1")"
print(result)

var value:Int? = nil
var result = value.map { String("result = \($0)") }
/// "nil"
print(result)

SequenceType進行map操作

我們可以使用map方法遍歷數(shù)組中的所有元素妻率,并對這些元素一一進行一樣的操作(函數(shù)方法)。map方法返回完成操作后的數(shù)組板祝。

image

我們可以用For-in完成類似的操作:

var values = [1,3,5,7]
var results = [Int]()
for var value in values {
    value *= 2
    results.append(value)
}
//"[2, 6, 10, 14]"
print(results)

這看起來有點麻煩宫静,我們得先定義一個變量var results然后將values里面的元素遍歷,進行我們的操作以后券时,將其添加進results孤里,我們比較下使用map又會怎么樣:

let results = values.map ({ (element) -> Int in
    return element * 2
})
//"[2, 6, 10, 14]"

我們向map傳入了一個閉包,對數(shù)組中的所有元素都 乘以2橘洞,將返回的新的數(shù)組賦值為results捌袜,是不是精簡了許多?還能更精簡炸枣!

精簡寫法

let results = values.map { $0 * 2 }
//"[2, 6, 10, 14]"

what the fuck...沉住氣虏等,讓我們一步步來解析怎么就精簡成這樣了,保證讓你神清氣爽适肠。翻開The Swift Programming Language中對于閉包的定義你就能找到線索霍衫。

第一步:

由于閉包的函數(shù)體很短,所以我們將其改寫成一行:

let results = values.map ({ (element) -> Int in return element * 2 })
//"[2, 6, 10, 14]"

第二步:

由于我們的閉包是作為map的參數(shù)傳入的侯养,系統(tǒng)可以推斷出其參數(shù)與返回值敦跌,因為其參數(shù)必須是(Element) -> Int類型的函數(shù)。因此逛揩,返回值類型柠傍,->及圍繞在參數(shù)周圍的括號都可以被忽略:

let results = values.map ({ element  in return element * 2 })
//"[2, 6, 10, 14]"

第三步:

單行表達式閉包可以通過省略return來隱式返回閉包的結(jié)果:

let results = values.map ({ element  in element * 2 })
//"[2, 6, 10, 14]"

由于閉包函數(shù)體只含有element * 2這單一的表達式麸俘,該表達式返回Int類型,與我們例子中map所需的閉包的返回值類型一致(其實是泛型)携兵,所以疾掰,可以省略return

第四步:

參數(shù)名稱縮寫(Shorthand Argument Names)徐紧,由于Swift自動為內(nèi)聯(lián)閉包提供了參數(shù)縮寫功能静檬,你可以直接使用$0,$1,$2...依次獲取閉包的第1,2并级,3...個參數(shù)拂檩。
如果您在閉包表達式中使用參數(shù)名稱縮寫,您可以在閉包參數(shù)列表中省略對其的定義嘲碧,并且對應(yīng)參數(shù)名稱縮寫的類型會通過函數(shù)類型進行推斷稻励。in關(guān)鍵字也同樣可以被省略:

let results = values.map ({ $0 * 2 })
//"[2, 6, 10, 14]"

例子中的$0即代表閉包中的第一個參數(shù)。

最后一步:

尾隨閉包愈涩,由于我們的閉包是作為最后一個參數(shù)傳遞給map函數(shù)的望抽,所以我們可以將閉包表達式尾隨:

let results = values.map (){ $0 * 2 }
//"[2, 6, 10, 14]"

如果函數(shù)只需要閉包表達式一個參數(shù),當您使用尾隨閉包時履婉,您甚至可以把()省略掉:

let results = values.map { $0 * 2 }
//"[2, 6, 10, 14]"

如果還有不明白的煤篙,可以多翻閱翻閱The Swift Programming Language

FlatMap

與map一樣毁腿,它可以用在 OptionalsSequenceType 上(如:數(shù)組辑奈、詞典等)。我們先來看看針對Optional的定義:

Optionals進行flatMap操作
public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {
    /// Returns `nil` if `self` is `nil`, `f(self!)` otherwise.
    @warn_unused_result
    public func flatMap<U>(@noescape f: (Wrapped) throws -> U?) rethrows -> U?
}

就閉包而言已烤,這里有一個明顯的不同鸠窗,這次flatMap期望一個 (Wrapped) -> U?)閉包。對于可選值胯究, flatMap 對于輸入一個可選值時應(yīng)用閉包返回一個可選值稍计,之后這個結(jié)果會被壓平,也就是返回一個解包后的結(jié)果裕循。本質(zhì)上臣嚣,相比 map,flatMap也就是在可選值層做了一個解包。

var value:String? = "1"
var result = value.map { Int($0)}
/// "Optional(Optional(1))"
print(result)

var value:String? = "1"
var result = value.flatMap { Int($0)}
/// ""Optional(1)"
print(result)

使用flatMap就可以在鏈式調(diào)用時费韭,不用做額外的解包工作:

var value:String? = "1"
var result = value.flatMap { Int($0)}.map { $0 * 2 }
/// ""Optional(2)"
print(result)

SequenceType進行flatMap操作

我們先來看看Swift中的定義

extension SequenceType {
    /// 返回一個將變換結(jié)果連接起來的數(shù)組
    /// `transform` over `self`.
    ///     s.flatMap(transform)
    /// is equivalent to
    ///     Array(s.map(transform).flatten())
    @warn_unused_result
    public func flatMap<S : SequenceType>(transform: (Self.Generator.Element) throws -> S) rethrows -> [S.Generator.Element]
}

extension SequenceType {
    /// 返回一個包含非空值的映射變換結(jié)果
    @warn_unused_result
    public func flatMap<T>(@noescape transform: (Self.Generator.Element) throws -> T?) rethrows -> [T]
}

通過這兩個描述,就提現(xiàn)了flatMapSequenceType的兩個作用:

一:壓平
var values = [[1,3,5,7],[9]]
let flattenResult = values.flatMap{ $0 }
/// [1, 3, 5, 7, 9]

二:空值過濾
var values:[Int?] = [1,3,5,7,9,nil]
let flattenResult = values.flatMap{ $0 }
/// [1, 3, 5, 7, 9]

Filter

同樣庭瑰,我先來看看Swift中的定義:

extension SequenceType {
    /// 返回包含原數(shù)組中符合條件的元素的數(shù)組
    /// Returns an `Array` containing the elements of `self`,
    /// in order, that satisfy the predicate `includeElement`.
    @warn_unused_result
    public func filter(@noescape includeElement: (Self.Generator.Element) throws -> Bool) rethrows -> [Self.Generator.Element]
}

filter函數(shù)接受一個(Element) -> Bool)的閉包星持,來判斷原數(shù)組中的元素是否符合條件,這個方法用來過濾數(shù)組中的一些元素再好不過了:

var values = [1,3,5,7,9]
let flattenResults = values.filter{ $0 % 3 == 0}
//[3, 9]

我們向flatMap傳入了一個閉包弹灭,篩選出了能被3整除的數(shù)據(jù)督暂。

Reduce

我們先來看下Swift中的定義:

extension SequenceType {
    /// Returns the result of repeatedly calling `combine` with an
    /// accumulated value initialized to `initial` and each element of
    /// `self`, in turn, i.e. return
    /// `combine(combine(...combine(combine(initial, self[0]),
    /// self[1]),...self[count-2]), self[count-1])`.
    @warn_unused_result
    public func reduce<T>(initial: T, @noescape combine: (T, Self.Generator.Element) throws -> T) rethrows -> T
}

給定一個初始化的combine結(jié)果揪垄,假設(shè)為result,從數(shù)組的第一個元素開始,不斷地調(diào)用combine閉包逻翁,參數(shù)為:(result饥努,數(shù)組中的元素),返回的結(jié)果值繼續(xù)調(diào)用combine函數(shù)八回,直至元素最后一個元素酷愧,返回最終的result值。來看下面的代碼(為了更方便你理解這個過程缠诅,代碼就不簡寫了):

var values = [1,3,5]
let initialResult = 0
var reduceResult = values.reduce(initialResult, combine: { (tempResult, element) -> Int in
    return tempResult + element
})
print(reduceResult)
//9

我們存在一個數(shù)組[1,3,5]溶浴,給定了一個初始化的結(jié)果 initialResult = 0,向reduce函數(shù)傳了 (tempResult, element) -> Int的閉包管引,tempResut便是每次閉包返回的結(jié)果值士败,并且其初始值為我們之前設(shè)置的initialResult0element即為我們數(shù)組中的元素(可能為1,3,5)褥伴。reduce會一直調(diào)用combine閉包谅将,直至數(shù)組最后一個元素。下面的代碼更形象地描述了整個過程重慢,這其實跟reduce所做的操作是等價的:

func combine(tempResult: Int, element: Int) -> Int  {
    return tempResult + element
}
reduceResult = combine(combine(combine(initialResult, element: 1), element: 3), element: 5)
print(reduceResult)
//9

作者:rayjuneWu
鏈接:http://www.reibang.com/p/87b97dfbf17b

二饥臂、map,filter伤锚,reduce 實踐

map:轉(zhuǎn)換擅笔,可以對數(shù)組中的元素格式進行轉(zhuǎn)換

//將Int數(shù)組轉(zhuǎn)換為String數(shù)組
//$0代表數(shù)組的元素
let array = [1, 2, 3, 4, 5 , 6, 7]
let result = array.map{
  String($0)
}

filter:過濾,可以對數(shù)組中的元素按照某種規(guī)則進行過濾

//在array中過濾出偶數(shù)
let result2 = array.filter{ 
  $0 % 2 == 0
}

reduce:計算 屯援,可以對數(shù)組中的元素進行計算

//計算數(shù)組array元素的和
//在這里$0和$1的意義不同猛们,$0代表元素計算后的結(jié)果,$1代表元素
//10代表初始化值狞洋,在這里可以理解為 $0初始值 = 10
let result3 = array.reduce(10){  
  $0 + $1
}

這三個函數(shù)介紹完了弯淘,可以看到這三個方法使用起來非常的便利,接下來我會寫一個計算文件夾大小的Demo
之前我已經(jīng)在沙盒中創(chuàng)建了log文件夾吉懊,里邊存放了四個文件,我們要做的是計算出log文件夾下.pdf格式的文件大小庐橙。

image

先寫兩個方法分別獲取文件夾的路徑和計算一個文件的大小

//獲取文件夾路徑
func getFolderPath(folderName: String) -> String{
        let path: NSString = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first!
        return  path.stringByAppendingPathComponent(folderName)
}
//計算一個文件的大小
func caculateFileSize(path: String) -> UInt64{
        let fileManager = NSFileManager.defaultManager()
        let dic: NSDictionary = try! fileManager.attributesOfItemAtPath(path)
        let size = dic.fileSize()
        return size

    }

使用map,filter借嗽,reduce計算文件夾下.pdf格式文件大小

  let folderPath = getFolderPath("log")
  let childFiles = NSFileManager.defaultManager().subpathsAtPath(folderPath)

  //使用filter過濾出.pdf格式的文件
  //在map方法體中态鳖,將文件數(shù)組轉(zhuǎn)換為size的數(shù)組
  //使用reduce計算size數(shù)組的和
  //最終返回reduce的計算結(jié)果
  let result = childFiles?.filter{
        ($0.componentsSeparatedByString(".")).last == "pdf"

  }.map({ (fileName) -> UInt64 in
        let filePath = folderPath + "/" + fileName
        return caculateFileSize(filePath)

  }) .reduce(0){
        $0 + $1

  }
  print(".pdf文件大小總和為----\(result)")

計算結(jié)果:

image

在代碼中使用filter方法后直接調(diào)用了map方法,這是因為高階函數(shù)支持鏈式調(diào)用恶导,高階函數(shù)的特性就是可以以一個函數(shù)或多個函數(shù)當參數(shù)浆竭,返回值也可以是一個函數(shù),如果你使用過AutoLayout庫 Masonry的話會很習(xí)慣這種寫法。

以上僅代表我的個人觀點邦泄,有不足的地方希望大家隨時與我溝通

參考:
Swift 燒腦體操(三) - 高階函數(shù)
Swift函數(shù)式編程實踐

作者:Lilin_Coder
鏈接:http://www.reibang.com/p/32c009fcb13d

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末删窒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子顺囊,更是在濱河造成了極大的恐慌肌索,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,464評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件特碳,死亡現(xiàn)場離奇詭異诚亚,居然都是意外死亡,警方通過查閱死者的電腦和手機测萎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評論 3 399
  • 文/潘曉璐 我一進店門亡电,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人硅瞧,你說我怎么就攤上這事份乒。” “怎么了腕唧?”我有些...
    開封第一講書人閱讀 169,078評論 0 362
  • 文/不壞的土叔 我叫張陵或辖,是天一觀的道長。 經(jīng)常有香客問我枣接,道長颂暇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,979評論 1 299
  • 正文 為了忘掉前任但惶,我火速辦了婚禮耳鸯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘膀曾。我一直安慰自己县爬,他們只是感情好,可當我...
    茶點故事閱讀 69,001評論 6 398
  • 文/花漫 我一把揭開白布添谊。 她就那樣靜靜地躺著财喳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪斩狱。 梳的紋絲不亂的頭發(fā)上耳高,一...
    開封第一講書人閱讀 52,584評論 1 312
  • 那天,我揣著相機與錄音所踊,去河邊找鬼泌枪。 笑死,一個胖子當著我的面吹牛秕岛,可吹牛的內(nèi)容都是我干的碌燕。 我是一名探鬼主播乍赫,決...
    沈念sama閱讀 41,085評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼陆蟆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起惋增,我...
    開封第一講書人閱讀 40,023評論 0 277
  • 序言:老撾萬榮一對情侶失蹤叠殷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后诈皿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體林束,經(jīng)...
    沈念sama閱讀 46,555評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,626評論 3 342
  • 正文 我和宋清朗相戀三年稽亏,在試婚紗的時候發(fā)現(xiàn)自己被綠了壶冒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,769評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡截歉,死狀恐怖胖腾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情瘪松,我是刑警寧澤咸作,帶...
    沈念sama閱讀 36,439評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站宵睦,受9級特大地震影響记罚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜壳嚎,卻給世界環(huán)境...
    茶點故事閱讀 42,115評論 3 335
  • 文/蒙蒙 一桐智、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧烟馅,春花似錦说庭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至穿撮,卻和暖如春缺脉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背悦穿。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評論 1 274
  • 我被黑心中介騙來泰國打工攻礼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人栗柒。 一個月前我還...
    沈念sama閱讀 49,191評論 3 378
  • 正文 我出身青樓礁扮,卻偏偏與公主長得像知举,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子太伊,可洞房花燭夜當晚...
    茶點故事閱讀 45,781評論 2 361

推薦閱讀更多精彩內(nèi)容