Swift學(xué)習(xí)筆記--Array篇

Array

[TOC]

如何創(chuàng)建Array

定義空數(shù)組

var array1: Array<Int> = Array<Int>()
var array2: [Int] = []
var array3 = array2

定義空數(shù)組并指定初始值

//[3,3,3]
var threeInts = [Int](repeating: 3, count: 3)
//[3,3,3,3,3,3]
var sixInts = threeInts + threeInts
//[1,2,3,4,5]
var fiveInts = [1,2,3,4,5]

兩個(gè)常用的Array屬性

.count 獲取數(shù)組中元素的個(gè)數(shù)
.isEmpty 數(shù)組是否為空

訪問(wèn)Array中的元素

  • 使用索引訪問(wèn)數(shù)組元素症昏,你必須確保索引的安全性长赞。如果索引超過(guò)了數(shù)組的范圍京办,程序就會(huì)崩潰
  • 使用range operator訪問(wèn)數(shù)組的一個(gè)范圍
fiveInts[0...2] //[1,2,3] 
fiveInts[0..<2] //[1,2]

range operator得到的颅崩,并不是一個(gè)Array,而是一個(gè)ArraySlice,即Array某一段內(nèi)容的View漠吻,它不真正保存數(shù)組的內(nèi)容,只保存這個(gè)view引用的數(shù)組的范圍

遍歷數(shù)組

除了訪問(wèn)單個(gè)元素外,另一類常用的需求就是順序訪問(wèn)數(shù)組中的每個(gè)成員艰管。在swift里,我們有三種基本的方法遍歷一個(gè)Array

  • for循環(huán):
    for value in fiveInts {
        print(value)
    }
    
    
  • 如果我們想在遍歷的時(shí)候同時(shí)獲得索引和值蒋川,可以只用enumerated()方法牲芋,他會(huì)返回一個(gè)Sequence對(duì)象,包含了每個(gè)成員的索引和值
    for (index, value) in fiveInts.enumerated() {
        print("\(index): \(value)")
    }
    
    
  • 借助closure捺球,我們還可以使用Array對(duì)象的forEach方法:
    fiveInts.forEach {print($0)}
    
    

添加和刪除元素

添加元素:

array1.append(1) //[1]
array1 += [2,3,4] //[1,2,3,4]

要在Array中間位置添加元素缸浦,可以使用insert方法:

//[1,2,3,4,5]
array1.insert(5, at: array1.endIndex)

刪除元素:

array1.remove(at: 4) //[1,2,3,4]

如果你想刪除最后一個(gè)元素,可以使用removeLast()方法

array1.removeLast() //[1,2,3]
array2.removeLast() //This will crash!!!

理解Array和NSArray的差異

同樣是數(shù)組類型氮兵,Swift中Array和Foundation 中的NSArray有著截然不同的語(yǔ)義和用法裂逐。

按值語(yǔ)義實(shí)現(xiàn)的Array

在Swift中,Array是按照值語(yǔ)義實(shí)現(xiàn)的泣栈,當(dāng)我們復(fù)制一個(gè)Array對(duì)象時(shí)卜高,會(huì)拷貝整個(gè)Array的內(nèi)容

var a = [1,2,3] // [1,2,3]
let copyA = a // [1,2,3]

a.append(4)
// a [1,2,3,4]
// copyA [1,2,3]
// copyA.append(4) Compile error

根據(jù)上面的代碼,有兩點(diǎn)值得說(shuō)明:

  • Swift 數(shù)組是否可以被修改完全是通過(guò)varlet關(guān)鍵字決定的南片,Array類型自身并不解決它是否可以被修改的問(wèn)題
  • 復(fù)制a并向a添加內(nèi)容之后掺涛,copyA的內(nèi)容并不會(huì)修改。但是疼进,Swift在復(fù)制Array時(shí)鸽照,同樣對(duì)Array的性能有所考量,他是用了copy on write的方式颠悬。如果你僅僅復(fù)制了Array而不對(duì)它修改時(shí)矮燎,真正的復(fù)制是不會(huì)發(fā)生的,兩個(gè)數(shù)組仍舊引用同一個(gè)內(nèi)存地址赔癌。只有當(dāng)你修改了其中一個(gè)Array的內(nèi)容時(shí)诞外,才會(huì)真正讓兩個(gè)Array對(duì)象分開(kāi)。為了看到這個(gè)過(guò)程灾票,我們先來(lái)實(shí)現(xiàn)一個(gè)方法峡谊,把保存Array內(nèi)容的地址變成了一個(gè)字符串:
func getBufferAddress<T>(of array: [T]) -> String {
    return array.withUnsafeBufferPointer {buffer in
        return String(describing: buffer.baseAddress)
    }
}

其中,withUnsafeBufferPointerArray的一個(gè)方法刊苍,它可以把保存Array內(nèi)容的地址既们,傳遞給它的closure參數(shù)。在我們的例子里正什,這個(gè)closure只是把Array的地址啥纸,變成了一個(gè)String對(duì)象。

getBufferAddress(of: a)
getBufferAddress(of: copyA)

a.append(4)

getBufferAddress(of: a)
getBufferAddress(of: copyA)

在我們運(yùn)行之后會(huì)發(fā)現(xiàn)婴氮,只有在給a添加內(nèi)容后斯棒,它才被重新分配了內(nèi)存地址盾致。

按引用語(yǔ)義實(shí)現(xiàn)的NSArray

在Foundation中,數(shù)組這個(gè)類型有兩點(diǎn)和SwiftArray是不同的:

  • 數(shù)組是否可以被修改時(shí)通過(guò)NSArray和NSMutableArray這兩個(gè)類型來(lái)決定的
  • NSArrayNSMutableArray都是類對(duì)象荣暮,復(fù)制他們執(zhí)行的是引用語(yǔ)義
// Mutable array [1,2,3]
let b = NSMutableArray(array: [1,2,3])
// Const array [1,2,3]
let copyB: NSArray = b

// [0,1,2,3]
b.insert(0, at: 0)
// [0,1,2,3]
copyB

在上面的代碼中可以看出庭惜,盡管我們?cè)趧?chuàng)建copyB時(shí),使用了NSArray穗酥,表明我們不希望它的值被修改护赊,由于這個(gè)賦值執(zhí)行的是應(yīng)用拷貝,因此砾跃,實(shí)際上它和b指向的是同一塊內(nèi)存空間骏啰。因此,當(dāng)我們修改b的內(nèi)容時(shí)蜓席,copyB也就間接受到了影響器一。

為了在拷貝NSArray對(duì)象時(shí),執(zhí)行值語(yǔ)義厨内,我們必須使用它的copy方法復(fù)制所有的元素:

let b = NSMutableArray(array: [1,2,3])
let copyB: NSArray = b
let deepCopyB = b.copy() as! NSArray

b.insert(0, at: 0) //[0,1,2,3]
copyB // [0,1,2,3]
deepCopyB // [1,2,3]

當(dāng)我們使用NSArrayNSMutableArray時(shí)祈秕,Swift中的varlet關(guān)鍵字就和數(shù)組是否可以被修改沒(méi)關(guān)系了。它們只控制對(duì)應(yīng)的變量是否可以被賦值成新的NSArrayNSMutableArray對(duì)象

用Swift的方式使用Array

絕大多數(shù)時(shí)候雏胃,其實(shí)你不需要[]

對(duì)于下標(biāo)訪問(wèn)數(shù)組元素這種老舊的形式请毛,Swift的開(kāi)發(fā)者應(yīng)該是不太喜歡的。不喜歡下標(biāo)操作符的理由是瞭亮,對(duì)于array[index]這樣的訪問(wèn)方仿,甚至都沒(méi)有使用optional來(lái)保護(hù)越界的情況

let a = [1,2,3]
type(of: a[1]) //Int.type

a[1]的類型是Int,而不是Optional<Int>,這說(shuō)明你必須小心翼翼的使用index來(lái)訪問(wèn)Array中的元素统翩,一旦index的值不正確仙蚜,你就需要承擔(dān)運(yùn)行崩潰的嚴(yán)重后果

我們可以采用其他的手段來(lái)替代下標(biāo)訪問(wèn)數(shù)組元素,比如我們想訪問(wèn)數(shù)組中的每一個(gè)元素時(shí):

a.forEach { print($0) }
    //or
for value in a {
}

當(dāng)我們要獲得數(shù)組中每一個(gè)元素的索引和值時(shí):

for (index,value) in a.enumerated() {}

當(dāng)我們要獲得數(shù)組中元素的位置時(shí)(例如查找等于1的元素的索引):

a.index( $0 == 1 )

index會(huì)返回一個(gè)Option<Int>,當(dāng)要查找的元素存在時(shí)厂汗,就返回該元素的索引委粉,否則,就返回nil娶桦。

當(dāng)我們要過(guò)濾數(shù)組中的某些元素時(shí)(例如贾节,去掉所有偶數(shù))

a.filter { $0 % 2 == 0 }

話說(shuō)回來(lái),給[]添加optional保護(hù)也不能解決安全問(wèn)題衷畦,因?yàn)橐坏┠鉬orce unwrapping 一個(gè)optional栗涂,就有可能會(huì)帶來(lái)一連串的force unwrapping。這不僅看上去不美觀祈争,從代碼表現(xiàn)的含義上來(lái)說(shuō)斤程,既然已經(jīng)準(zhǔn)備好要為結(jié)果全權(quán)負(fù)責(zé)了。又何必要再讓你多執(zhí)行一不force unwrapping呢铛嘱。

一些安全周到的方法

[]的高風(fēng)險(xiǎn)形成鮮明對(duì)比的是暖释,對(duì)于那些可以生成優(yōu)秀代碼的方法袭厂,Swift則考慮的面面俱到墨吓。例如:
訪問(wèn)數(shù)組中第一個(gè)和最后一個(gè)元素的firstlast屬性球匕,當(dāng)Array為空時(shí),他們的值都是nil:

a.first //1
a.last //3
type(of: a.first) // Optional<Int>.Type

另外一個(gè)值得一提的是在Array末尾刪除元素帖烘。Swift為這個(gè)動(dòng)作提供了兩個(gè)API:

  • removeLast,你需要自行確保數(shù)組中有元素亮曹,否則會(huì)引發(fā)運(yùn)行時(shí)錯(cuò)誤
  • popLast,如果數(shù)組為空,會(huì)返回nil

為什么要如此呢秘症? 一個(gè)通俗的解釋就是照卦,為了表意更清晰的代碼。
當(dāng)你基于Array實(shí)現(xiàn)諸如棧這樣后入先出的數(shù)據(jù)結(jié)構(gòu)時(shí)乡摹,彈出一個(gè)元素并判斷是否為空是一個(gè)常規(guī)的操作役耕,所以popLast返回了一個(gè)optional。而對(duì)于更一般的"刪除數(shù)組中最后一個(gè)元素"這樣的行為聪廉,Swift認(rèn)為這沒(méi)有任何更具體的使用場(chǎng)景瞬痘,你應(yīng)該自己對(duì)這樣的"低級(jí)錯(cuò)誤"負(fù)責(zé)。

通過(guò)closure參數(shù)化對(duì)數(shù)組元素的變形操作

當(dāng)你對(duì)Array做一些處理的時(shí)候板熊,像C語(yǔ)言中類似的循環(huán)和下標(biāo)框全,都不是理想的選擇。Swift有一套自己的"現(xiàn)代化"手段干签。簡(jiǎn)單來(lái)說(shuō)津辩,就是通過(guò)closure來(lái)參數(shù)化對(duì)數(shù)組的操作行為。

從循環(huán)到map

假設(shè)我們有一個(gè)簡(jiǎn)單的Fibonacci序列:[0,1,1,2,3,5]容劳。如果我們要計(jì)算每個(gè)元素的平方喘沿,怎么辦呢?
一個(gè)最樸實(shí)的做法是for循環(huán):

var fibonacci = [0,1,1,2,3,5]
var squares = [Int]()

for value in fibonacci {
    squares.append(value * value)
}  

如果你覺(jué)得這還不是個(gè)足夠引起你注意的問(wèn)題竭贩,那么蚜印,當(dāng)我們要定義一個(gè)常量squares的時(shí)候,上面的代碼就完全無(wú)法勝任了娶视。怎么辦呢晒哄?先看解決辦法:

//[0,1,1,4,9,25]
let constSquares = fibonacci.map { $0 * $0 }

上面的代碼,和之前的for循環(huán)執(zhí)行的結(jié)果是相同的肪获。顯然寝凌,它比for循環(huán)更具表現(xiàn)力,并且也能把我們期望的結(jié)果定義成常量孝赫。當(dāng)然较木,map并不是什么魔法,無(wú)非就是把for循環(huán)執(zhí)行的邏輯青柄,封裝在了函數(shù)里伐债,這樣我們就可以把函數(shù)的返回值賦值給常量了预侯,我們可以通過(guò)extension很簡(jiǎn)單的自己來(lái)實(shí)現(xiàn)map

extension Array {
    func myMap<T>(_ transform: (Element) -> T) -> [T] {
        var tmp: [T] = []
        tmp.reserveCapacity(count)
        for value in self {
            tmp.append(transform(value))
        }
        return tmp
    }
}

雖然和Swift標(biāo)準(zhǔn)庫(kù)相比,MyMap的實(shí)現(xiàn)中去掉了和異常聲明相關(guān)的部分峰锁。但它已經(jīng)足以表現(xiàn)map的核心實(shí)現(xiàn)過(guò)程了萎馅。除了在append之前使用了reserveCapacity給新數(shù)組預(yù)留了空間之外,它的實(shí)現(xiàn)過(guò)程和一開(kāi)始我們使用的for循環(huán)沒(méi)有任何差別.

如果你還不了解Element也沒(méi)關(guān)系虹蒋,把它理解為Array中元素類型的替代符就好了再后面我們講到Sequence類型的時(shí)候糜芳,會(huì)專門(mén)提到它。

完成后魄衅,當(dāng)我們?cè)趐layground里測(cè)試的時(shí)候:

// [0,1,1,4,9,25]
let constSuquence1 = fibonacci.myMap { $0 * $0 }

就會(huì)發(fā)現(xiàn)執(zhí)行結(jié)果和之前的constSequence是一樣的了峭竣。

參數(shù)化數(shù)組元素的執(zhí)行動(dòng)作

其實(shí),仔細(xì)觀察myMap的實(shí)現(xiàn)晃虫,就會(huì)發(fā)現(xiàn)它最大的意義皆撩,就是保留了遍歷Array的過(guò)程,而把執(zhí)行的動(dòng)作留給了myApp的調(diào)用者通過(guò)參數(shù)去定制哲银。而這扛吞,就是我們一開(kāi)始提到的用closure來(lái)參數(shù)化對(duì)數(shù)組的操作行為的含義。

有了這樣的思路之后盘榨,我們就可以把各種常用的帶有遍歷行為的操作喻粹,定制成多種不同的遍歷"套路",而把對(duì)數(shù)組中每一個(gè)元素的處理動(dòng)作留給函數(shù)的調(diào)用者草巡。但是別急守呜,在開(kāi)始自動(dòng)動(dòng)手造輪子之前,Swift library已經(jīng)為我們準(zhǔn)備了一些山憨,例如:
首先查乒,是找到最小、最大值郁竟,對(duì)于這類操作來(lái)說(shuō)玛迄,只要數(shù)組中的元素實(shí)現(xiàn)了"Equatable"protocol,我們甚至無(wú)需定義對(duì)元素的具體操作:

fibonacci.min() // 0
fibonacci.max() // 5

使用minmax很安全棚亩,因?yàn)楫?dāng)數(shù)組為空時(shí)蓖议,這兩個(gè)方法將返回nil.
其次,過(guò)濾出滿足特定條件的元素讥蟆,我們只要通過(guò)參數(shù)制定篩選規(guī)則就好了:

fibonacci.filter { $0 % 2 == 0 }

第三勒虾,比較數(shù)組相等或以特定元素開(kāi)始。對(duì)這類操作瘸彤,我們需要提供兩個(gè)內(nèi)容修然,一個(gè)是要比較的數(shù)組,另一個(gè)則是比較的規(guī)則:

//false
fibonacci.elementsEqual([0,1,1], by: { $0 == $1 })
//true
fibonacci.starts(with: [0,1,1], by: { $0 == $1 })

第四,最原始的for循環(huán)的替代品:

fibonacci.forEach { print($0) }
// 0 
// 1
// ...

要注意它和map的一個(gè)重要區(qū)別:forEach并不處理closure參數(shù)的返回值愕宋。因此它只適合用來(lái)對(duì)數(shù)組中的元素進(jìn)行一些操作玻靡,而不能用來(lái)產(chǎn)生返回結(jié)果。

第五中贝,對(duì)數(shù)組進(jìn)行排序囤捻,這時(shí),我們需要通過(guò)參數(shù)制定的是排序規(guī)則:

// [0,1,1,2,3,5]
fibonacci.sorted()
// [5,3,2,1,1,0]
fibonacci.sorted(by: >)

let privot = fibonacci.partition(by: { $0 < 1 })
fibonacci[0 ..< privot] // [1,1,2,3,5]
fibonacci[privot ..< fibonacci.endIndex] //[0]

其中 sorted(by:)的用法是很直接的雄妥,它默認(rèn)采用升序排列最蕾。同事依溯,也允許我們通過(guò)by自定義排序規(guī)則老厌。在這里>{ $0 > $1 }的簡(jiǎn)寫(xiě)形式。Swift中很多在不影響語(yǔ)義的情況下的簡(jiǎn)寫(xiě)形式黎炉。

partition(by:)則會(huì)先對(duì)傳遞給它的數(shù)組進(jìn)行重排枝秤,然后根據(jù)指定的條件在重排的結(jié)果中返回一個(gè)分界點(diǎn)位置。這個(gè)分界點(diǎn)分開(kāi)的兩部分中慷嗜,前半部分的元素都不滿足指定條件淀弹;后半部分都滿足指定條件。而后庆械,我們就可以使用range operator來(lái)訪問(wèn)者兩個(gè)區(qū)間形成的Array對(duì)象薇溃。大家可以根據(jù)例子中注釋的結(jié)果,來(lái)理解partition的用法缭乘。

第六沐序,是把數(shù)組中的所有內(nèi)容,"合并"成某種形式的值堕绩,對(duì)這類操作策幼,我們需要指定的,是合并前的初始值奴紧,以及"合并"的規(guī)則特姐。例如,我們計(jì)算fibonacci中所有元素的和:

fibonacci.reduce(0, +) //12

在這里黍氮,初始值為0唐含,和第二個(gè)參數(shù)+,則是 { $0 + $1 }的縮寫(xiě).

通過(guò)這些例子,你應(yīng)該能感覺(jué)到了沫浆,這些通過(guò)各種形式封裝了遍歷動(dòng)作的方法捷枯,他們之中的任何一個(gè),都比直接通過(guò)for循環(huán)實(shí)現(xiàn)具有更強(qiáng)的表現(xiàn)力件缸。這些API铜靶,開(kāi)始讓我們的代碼從面向機(jī)器的,轉(zhuǎn)變成面向業(yè)務(wù)需求的。因此争剿,在Swift里已艰,你應(yīng)該試著讓自己轉(zhuǎn)變觀念,當(dāng)你面對(duì)一個(gè)Array時(shí)蚕苇,你真的幾乎可以忘記下標(biāo)和循環(huán)了哩掺。

區(qū)分修改外部變量和保存內(nèi)部狀態(tài)

當(dāng)我們使用上面提到的這些帶有closure參數(shù)的Array方法時(shí),一個(gè)不好的做法就是通過(guò)closure去修改外部變量涩笤,并依賴這種副作用產(chǎn)生的結(jié)果嚼吞。來(lái)看一個(gè)例子:

var sum = 0
let constSquare2 = fibonacci.map { (fib: Int) -> Int in 
    sum += fib
    return fib * fib
}

在這個(gè)例子里,map的執(zhí)行產(chǎn)生了一個(gè)副作用蹬碧,就是對(duì)fibonacci中所有的元素求和舱禽。這不是一個(gè)好的方法,我們應(yīng)該避免這樣恩沽。你應(yīng)該單獨(dú)使用reduce來(lái)完成這個(gè)操作誊稚,或者如果一定要在closure參數(shù)里修改外部變量,哪怕用forEach也比map更好的方案罗心。

但是里伯,在函數(shù)實(shí)現(xiàn)內(nèi)部,專門(mén)用一個(gè)外部變量來(lái)保存closure參數(shù)的執(zhí)行狀態(tài)渤闷,則是一個(gè)常用的實(shí)現(xiàn)技法疾瓮。例如,我們要?jiǎng)?chuàng)建一個(gè)新的數(shù)組飒箭,其中每個(gè)值狼电,都是數(shù)組當(dāng)前位置和之前所有元素的和,可以這樣:

extension Array {
    func accumulate<T>(_ initial: T, _ nextSum: (T, Element) -> T) -> [T] {
        var sum = initial
        return map { next in 
            sum = nextSum(sum, next)
            return sum
        }
    }
}

在上面這個(gè)例子里补憾,我們利用map的closure參數(shù)捕獲了sum漫萄,這樣就保存了每一次執(zhí)行map時(shí),之前所有元素的和盈匾。

//[0,1,2,4,7,12]
fibonacci.accumulate(0, +)

Filter/Reduce/FlatMap的實(shí)現(xiàn)和拓展

這一章著重了解三個(gè)比較重要的ArrayAPI,filter/reduce/flatMap,它們和我們?cè)谏弦还?jié)中實(shí)現(xiàn)的map一起腾务,形成了各種Array操作的基礎(chǔ)。

filter和filter類似的語(yǔ)義

之前削饵,我們提到過(guò)filter的用法岩瘦,用于在Array中,過(guò)濾滿足特定條件的元素窿撬。而這個(gè)條件启昧,就是通過(guò)filter的closure參數(shù)來(lái)確定的:

var fibonacci = [0,1,1,2,3,5]
//[0,2]
fibonacci.filter { %0 % 2 == 0}

按照上一節(jié)中實(shí)現(xiàn)的map的思路,我們可以自己來(lái)實(shí)現(xiàn)一個(gè)filter

extension Array {
    func myFilter(_ predicate: (Element) -> Bool) -> [Element] {
        var temp: [Element] = []

        for value in self where predicate(value) {
            temp.append(value)
        }
        
        return temp
    }
}

在上面的實(shí)現(xiàn)里劈伴,最核心的環(huán)節(jié)就是通過(guò)where條件的for循環(huán)找到原數(shù)組中符合條件的元素密末,然后把它們一一添加到temp中,并最終返回給函數(shù)的調(diào)用者。然后严里,我們測(cè)試下myFilter

fibonacci.myFilter { $0 % 2 == 0 } // [0,2]

結(jié)果應(yīng)該是和標(biāo)準(zhǔn)庫(kù)中的自帶的filter是一樣的新啼。理解filter之后,我們就可以自行定義一些標(biāo)準(zhǔn)庫(kù)中沒(méi)有的方法刹碾。例如:
剔除掉數(shù)組中滿足條件的元素:

extension Array {
    func reject(_ predicate: (Element) -> Bool) -> [Element]{
        return filter { !predicate($0) }
    }
}

我們只要把調(diào)用轉(zhuǎn)發(fā)給filter,然后把指定的條件取反就好了燥撞。這樣,提出元素的代碼語(yǔ)義上就會(huì)更好看一些:

fibonacci.reject { $0 % 2 == 0 } //[1,1,3,5]

另一個(gè)基于filter語(yǔ)義的常用操作是判斷數(shù)組中是否存在滿足條件的元素迷帜。下面的代碼可以完成任務(wù):

fibonacci.filter { $0 % 2 == 0}.count > 0 //true

但這樣做在性能上并不理想物舒,因?yàn)榧幢阏业搅藵M足條件的元素,也要遍歷完整個(gè)數(shù)組戏锹,這顯然是沒(méi)有必要的冠胯。Swift標(biāo)準(zhǔn)庫(kù)中,提供了一個(gè)更方便的方法:

fibonacci.containts { $0 % 2 == 0} //true

contains的一個(gè)好處就是只要遇到滿足條件的元素景用,函數(shù)的執(zhí)行就終止了涵叮。基于這個(gè)contains伞插,我們還可以給Array添加一個(gè)新的方法,用來(lái)判斷Array中所有的元素是否滿足特定的條件

extension Array {
    func allMatch(_ predicate: (Element) -> Bool) -> Bool{
        return !contains { !predicate:$0 }
    }
}

allMatch的實(shí)現(xiàn)里盾碗,只要沒(méi)有不滿足條件的元素媚污,也就是所有的元素都滿足條件了。我們可以用下面的代碼測(cè)試一下:

let events = [2,4,6,8]
events.allMatch { $0 % 2 == 0 } // true

reduce和reduce相關(guān)的語(yǔ)義

除了用一個(gè)數(shù)組生成一個(gè)新的數(shù)組廷雅,有時(shí)耗美,我們會(huì)希望把一個(gè)數(shù)組變成某種形式的值。例如,之前我們提到的求和:

fibonacci.reduce(0, +) // 12

了解reduce的進(jìn)一步用法之前航缀,我們先自己實(shí)現(xiàn)一個(gè)

extension Array {
    func myReduce<T>(_ initial: T, _ next: (T, Element) -> T) -> T {
        var temp = initial
        for value in self {
            temp = next(temp,value)
        }   
        return temp
    }
}

從上面可以看出商架,reduce的實(shí)現(xiàn)也沒(méi)有什么神奇之處。無(wú)非就是把for循環(huán)迭代相加的過(guò)程給封裝了起來(lái)芥玉。然后蛇摸,用下面的代碼測(cè)試一下,就會(huì)發(fā)現(xiàn)和標(biāo)準(zhǔn)庫(kù)里的reduce一樣了灿巧。

fibonacci.myReduce(0,+) //12

除了求和以外赶袄,我們還可以把fibonaccireduce成一個(gè)字符串

let str = fibonacci.myReduce("") { str, num in
    return str + "\(num)"
}
// "0 1 1 2 3 5"

甚至,我們還可以用reduce模擬mapfilter的實(shí)現(xiàn):

extension Array {
    func myMap2<T>(_ transform: (Element) -> T) -> [T] {
        return reduce([],{ $0 + transform($1) })
    }
    
    func myFilter2(_ predicate: (Element) -> Bool) -> [ELement] {
        return reduce([],{ predicate($1) ? $0 + [$1] : $0            })
    }
}   

然后簡(jiǎn)單測(cè)試一下:

//[0,1,1,4,9,25]
fibonacci.myMap2 { $0 * $0 }
//[0,2]
fibonacci.myFilter2 { $0 % 2 == 0 }

他們的結(jié)果和標(biāo)準(zhǔn)庫(kù)中的map 和 filter是一樣的抠藕。但是饿肺,這種看似優(yōu)雅的寫(xiě)法卻沒(méi)有想象中的那么好。在他們內(nèi)部的reduce調(diào)用中盾似,每一次$0的參數(shù)都是一個(gè)新建的數(shù)組敬辣,因此整個(gè)算法的復(fù)雜度是O(n^2),而不是for循環(huán)版本的O(n)。所以溉跃,這樣的實(shí)現(xiàn)方法最好還是用來(lái)作為理解reduce用法的例子

后面會(huì)陸續(xù)給出我在Swift學(xué)習(xí)中的筆記汰聋。如果有什么錯(cuò)誤,請(qǐng)?jiān)谙路皆u(píng)論處給出喊积,我會(huì)及時(shí)進(jìn)行修改烹困。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市乾吻,隨后出現(xiàn)的幾起案子髓梅,更是在濱河造成了極大的恐慌,老刑警劉巖绎签,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件枯饿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡诡必,警方通過(guò)查閱死者的電腦和手機(jī)奢方,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)爸舒,“玉大人蟋字,你說(shuō)我怎么就攤上這事∨っ悖” “怎么了鹊奖?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)涂炎。 經(jīng)常有香客問(wèn)我忠聚,道長(zhǎng),這世上最難降的妖魔是什么唱捣? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任两蟀,我火速辦了婚禮,結(jié)果婚禮上震缭,老公的妹妹穿的比我還像新娘赂毯。我一直安慰自己,他們只是感情好蛀序,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布欢瞪。 她就那樣靜靜地躺著,像睡著了一般徐裸。 火紅的嫁衣襯著肌膚如雪遣鼓。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天重贺,我揣著相機(jī)與錄音骑祟,去河邊找鬼回懦。 笑死,一個(gè)胖子當(dāng)著我的面吹牛次企,可吹牛的內(nèi)容都是我干的怯晕。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼缸棵,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼舟茶!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起堵第,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤吧凉,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后踏志,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體阀捅,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年针余,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了饲鄙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡圆雁,死狀恐怖忍级,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情摸柄,我是刑警寧澤颤练,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站驱负,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏患雇。R本人自食惡果不足惜跃脊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望苛吱。 院中可真熱鬧酪术,春花似錦、人聲如沸翠储。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)援所。三九已至庐舟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間住拭,已是汗流浹背挪略。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工历帚, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人杠娱。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓挽牢,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親摊求。 傳聞我的和親對(duì)象是個(gè)殘疾皇子禽拔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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