簡介
這是一個(gè)Swift語言教程受神,基于最新的iOS 9,Xcode 7.3和Swift 2.2茎匠,會為你介紹Swift編程非常基礎(chǔ)的內(nèi)容辜荠。從電腦如何工作的全程基本原理到語言結(jié)構(gòu)汽抚,你會足夠了解這門語言,來處理數(shù)據(jù)和管理代碼的行為伯病。
快速鏈接
- 一個(gè)下午讓你掌握Swift基礎(chǔ) ( 1/9 ) 編程本質(zhì) & Playground基礎(chǔ)
- 一個(gè)下午讓你掌握Swift基礎(chǔ) ( 2/9 ) 變量 & 常量
- 一個(gè)下午讓你掌握Swift基礎(chǔ) ( 3/9 ) 數(shù)字類型 & 操作
- 一個(gè)下午讓你掌握Swift基礎(chǔ) ( 4/9 ) 字符串
- 一個(gè)下午讓你掌握Swift基礎(chǔ) ( 5/9 ) 做判斷
- 一個(gè)下午讓你掌握Swift基礎(chǔ) ( 6/9 ) 重復(fù)步驟
- 一個(gè)下午讓你掌握Swift基礎(chǔ) ( 7/9 ) 函數(shù)
- 一個(gè)下午讓你掌握Swift基礎(chǔ) ( 8/9 ) 閉包
- 一個(gè)下午讓你掌握Swift基礎(chǔ) ( 9/9 ) 可選值
- Swift 初學(xué)者 ( 11/12 ) 字典
- Swift 初學(xué)者 ( 12/12 ) 集合
第二部分:集合類型
到目前為止造烁,你看到的數(shù)據(jù)主要都是單個(gè)形式。盡管元祖可以有多塊數(shù)據(jù)午笛,但你必須指定大胁洋;帶有三個(gè)字符串的元祖和帶有兩個(gè)字符串的元祖是完全不同的類型药磺,他們之間的轉(zhuǎn)換不是件小事告组。
在這個(gè)部分,你會學(xué)習(xí)有關(guān) Swift 中集合類型(collection types)的內(nèi)容癌佩。集合是靈活的“容器”木缝,可以讓你一起存儲任意多個(gè)值便锨。
Swift 中有三個(gè)集合類型:數(shù)組,字典和 sets我碟。你會在下面三篇文章用這個(gè)順序來學(xué)習(xí)他們:
- 教程 10放案,數(shù)組
- 教程 11,字典
- 教程 12矫俺,sets
集合類型們有相似的外觀吱殉,但非常不同的用法。當(dāng)你讀完三篇文章后厘托,把差別記在腦海里友雳,你要開始養(yǎng)成隨時(shí)知道應(yīng)該用哪種類型的那種感覺。
作為探索集合類型之間區(qū)別的一部分铅匹,我們也會考慮性能:集合執(zhí)行特定操作有多快押赊,比如向集合中添加元素或在其中搜索。
討論性能的常用方式是用大O符號(big-O notation)伊群。如果你還不熟悉它考杉,讀一讀簡短的介紹吧。
介紹大O符號
大O符號是描述運(yùn)行時(shí)間(running time)的一種方式舰始,也就是一個(gè)操作用了多長時(shí)間來完成崇棠。想法是操作用的確切時(shí)間并不重要;重要的是在規(guī)模上的相對差異丸卷。
想想你有一列名字枕稀,是某種隨機(jī)的順序,現(xiàn)在你需要找到列表上的第一個(gè)名字谜嫉。這列只有一個(gè)名字或一百萬個(gè)名字都不重要—找到最開始的那個(gè)名字總是花同樣多的時(shí)間萎坷。這是一次恒量時(shí)間(constant time)操作的例子,或者是大O符號中的 O(1)沐兰。
現(xiàn)在假設(shè)你需要找到列表中的一個(gè)特定名字哆档。你需要掃描整個(gè)列表,看每一個(gè)單獨(dú)的名字住闯,直到你找到一個(gè)匹配或抵達(dá)終點(diǎn)瓜浸。這次也是,我們不關(guān)心這次用的確切時(shí)間比原,只關(guān)心相比其他操作的相對時(shí)間插佛。
要計(jì)算運(yùn)行時(shí)間,要考慮工作的單位量窘。你需要看每個(gè)名字雇寇,所以把這認(rèn)為是每個(gè)名字的工作的一個(gè)“單位”。如果你有 100 個(gè)名字,那就是 100 個(gè)工作單位锨侯。如果你把名字的個(gè)數(shù)翻倍到 200嫩海;那怎么改變工作的量呢?他也讓工作的量翻倍了识腿。相似的出革,如果你讓名字的數(shù)量變成四倍造壮,那也讓工作的量變成四倍渡讼。
這是線性時(shí)間(linear time)操作的例子,也就是大O符號中的 O(N)耳璧。輸入的大小是變量 N成箫,表示操作花的時(shí)間的量也是 N。這里有一個(gè)直接的旨枯、線性的關(guān)系蹬昌,在輸入尺寸(列表里名字的數(shù)量)和搜索一個(gè)名字會用的時(shí)間之間。
你可以看到為什么恒量時(shí)間操作在 O(1) 里有數(shù)字 1攀隔。不論怎么樣皂贩,他們只是一個(gè)單獨(dú)的工作單位!
你可以通過搜索網(wǎng)絡(luò)來讀更多大O符號相關(guān)的內(nèi)容昆汹。這個(gè)教程里你只需要恒量時(shí)間和線性時(shí)間明刷,但還有其他的超出這個(gè)范圍的復(fù)雜時(shí)間(time complexities)。
大O符號在處理集合類型的時(shí)候尤其重要满粗,因?yàn)榧峡梢源鎯艽罅康臄?shù)據(jù)辈末,并且你需要知道在添加、刪除和編輯值得時(shí)候的運(yùn)行時(shí)間映皆。
例如挤聘,如果集合類型 A 搜索有恒量時(shí)間,集合類型 B 搜索有線性時(shí)間捅彻,你選擇使用哪個(gè)會基于你計(jì)劃搜索的量級组去。
數(shù)組
數(shù)組(Arrays)是你會在 Swift 里遇到的最通用的集合類型。數(shù)組是類型化的步淹,就像常規(guī)的變量和常量从隆,存儲多個(gè)值比如一個(gè)簡單的列表。
在你創(chuàng)建第一個(gè)數(shù)組前贤旷,花一點(diǎn)時(shí)間考慮一下細(xì)節(jié)广料,數(shù)組是什么以及為什么你想要用它。
什么是數(shù)組幼驶?
數(shù)組是同類型的值的有序集合艾杏。數(shù)組里的元素是零索引(zero-indexed),表示第一個(gè)元素的索引是 0盅藻,第二個(gè)元素的索引是 1购桑,以此類推畅铭。知道這一點(diǎn),你可以算出最后一個(gè)元素的索引是數(shù)組中的值的個(gè)數(shù)減 1勃蜘。
這個(gè)數(shù)組里有五個(gè)元素硕噩,索引值是 0-4。
還有缭贡,所有值都是 String 類型炉擅。你不能向一個(gè)保存字符串的數(shù)組添加非字符串類型。注意相同的值可以出現(xiàn)多次阳惹。
什么時(shí)候數(shù)組有用谍失?
當(dāng)你想把一些東西用一個(gè)特定順序保存的時(shí)候,數(shù)組很有用莹汤。你想要順序可能是因?yàn)橐诸愓碓乜煊悖蛘咝枰ㄟ^索引取出元素,而不是遍歷一整個(gè)數(shù)組纲岭。
例如抹竹,如果你在存儲高分?jǐn)?shù)據(jù),順序不影響止潮。你會想要最高分出現(xiàn)在列表的第一個(gè)(也就是在 0 索引)窃判,第二高的分?jǐn)?shù)次之,以此類推沽翔。
可修改 vs 不可修改數(shù)組
就像你之前已經(jīng)讀到的類型兢孝,比如 String 或 Int,當(dāng)你創(chuàng)建數(shù)組的時(shí)候仅偎,必須聲明它為常量或變量跨蟹。
如果數(shù)組創(chuàng)建之后不需要改變,你應(yīng)該用 let 聲明它為常量橘沥,讓它不可修改窗轩。如果你需要添加、移除或更新數(shù)組里的值座咆,你應(yīng)該聲明它為變量來創(chuàng)建一個(gè)可修改的數(shù)組痢艺。
創(chuàng)建數(shù)組
在這個(gè)部分,你會練習(xí)聲明數(shù)組和給他們賦值初始值介陶。
顯示聲明
你可以顯示聲明一個(gè)數(shù)組堤舒,或利用 Swift 的類型推斷特色。這是一個(gè)顯示聲明的例子:
let numbers: Array<Int>
尖括號里的類型定義了數(shù)組可以存儲的值類型哺呜,編譯器在你向數(shù)組添加元素的時(shí)候會這么強(qiáng)迫你舌缤。例如,如果你想添加一個(gè)字符串,編譯器會返回一個(gè)錯誤国撵,你的代碼就無法編譯了陵吸。這個(gè)強(qiáng)大的強(qiáng)迫機(jī)制叫做 generics,你會在后面學(xué)習(xí)更多介牙。
在這個(gè)例子里壮虫,你把 numbers 聲明為一個(gè)數(shù)組,只能存儲 Int 值环础。你把這個(gè)數(shù)組定義為常量囚似,所以它的值不能改變。
推斷聲明
Swift 也能從類型的初始化中推斷數(shù)組的類型:
let inferredNumbers = Array<Int>()
這里 inferredNumbers 也是一個(gè)整數(shù)的數(shù)組喳整。如果你跟著做了谆构,把這行代碼打到了 playground 里,你會注意到結(jié)果區(qū)域的 []框都。這是 Swift 對于空數(shù)組的表示。
另外呵晨,定義數(shù)組簡短點(diǎn)的方式是用方括號圍繞類型魏保,像這樣:
let alsoInferredNumbers = [Int]()
這是對于數(shù)組的最常用的聲明形式,你會在本教程中貫穿使用摸屠。
數(shù)組字面值
當(dāng)你聲明數(shù)組的時(shí)候谓罗,你可能想給它初始值。你可以用數(shù)組字面值(array literals)季二,是一個(gè)提供數(shù)組值得簡潔的方式檩咱。一個(gè)數(shù)組字面值是一列值,用逗號間隔胯舷,被方括號包圍:
let evenNumbers = [2, 4, 6, 8]
因?yàn)槁暶髦话麛?shù)刻蚯,Swift 推斷 evenNumbers 的類型為 Int 值的數(shù)組,也就是 [Int]桑嘶。
創(chuàng)建一個(gè)數(shù)組炊汹,把值全部設(shè)置為一個(gè)默認(rèn)值也是可能的:
let allZeros = [Int](count: 5, repeatedValue: 0)
// > [0, 0, 0, 0, 0]
因?yàn)?[Int]—包含方括號—只是一個(gè)類型,你在這個(gè)例子里調(diào)用了它的初始化逃顶。兩個(gè)參數(shù)制定了個(gè)數(shù)讨便,和將要被重復(fù)的值。
你到目前為止創(chuàng)建的數(shù)組都是不可修改的以政,因?yàn)槟惆阉麄冑x值為常量霸褒。對于不會改變的數(shù)組這是一個(gè)好的習(xí)慣。例如,考慮這個(gè)數(shù)組:
let vowels = ["A", "E", "I", "O", "U"]
vowels (元音)是字符串的數(shù)組,并且它的值不會被改變乖菱。但這很好呀溉潭,因?yàn)樵舻牧斜聿粫?jīng)常變病游!
訪問元素
能夠創(chuàng)建數(shù)組并沒有什么用律歼,除非你知道怎么從數(shù)組里獲取值胡桃。在這個(gè)部分旁蔼,你會學(xué)習(xí)到訪問數(shù)組里元素的幾種不同方式梳凛。
使用 properties 和方法
假設(shè)你在創(chuàng)建一個(gè)紙牌游戲耿币,希望用一個(gè)數(shù)組存儲選手的名字。選手加入或離開游戲的時(shí)候要改變這個(gè)列表韧拒,所以要聲明一個(gè)可被修改的數(shù)組:
var players = ["Alice", "Bob", "Cindy", "Dan"]
這個(gè)例子里淹接,players 是一個(gè)可變數(shù)組,因?yàn)槟惆阉峙錇樽兞颗岩纭T谟螒蜷_始前塑悼,需要確保有足夠的選手】簦可以用 isEmpty property 來檢測是不是有至少一名選手:
print(players.isEmpty)
// > false
數(shù)組不是空的厢蒜,但要開始游戲的話至少要兩個(gè)選手∨胫玻可以用 count property 來獲取選手的數(shù)量:
if players.count < 2 {
print("我們需要至少兩名選手斑鸦!")
} else {
print("開始吧!")
}
// > 開始吧草雕!
是時(shí)候開始游戲了巷屿!你決定游戲的順序是根據(jù)數(shù)組里的名字排列順序。那怎么得到第一個(gè)選手的名字呢墩虹?
數(shù)組提供了 first property 來獲取數(shù)組的第一個(gè)對象:
var currentPlayer = players.first
打印 currentPlayer 的值顯示了有趣的東西:
print(currentPlayer)
// > Optional("Alice")
frist property 實(shí)際上返回了一個(gè)可選值嘱巾,因?yàn)槿绻麛?shù)組是空的話,first 會返回 nil诫钓。
相似的旬昭,數(shù)組有一個(gè) last property 返回?cái)?shù)組的最后一個(gè)值,如果數(shù)組是空的話就返回 nil:
print(players.last)
// > Optional("Dan")
從數(shù)組獲取值的另一種方式是通過調(diào)用 minElement()尖坤。這個(gè)方法返回?cái)?shù)組里有最低的值的元素——不是最低的索引稳懒!如果數(shù)組包含的是字符串,就返回按字母表順序最低的字符串慢味,在這個(gè)例子里是"Alice":
currentPlayer = players.minElement()
print(currentPlayer)
// > Optional("Alice")
很明顯场梆,frist 和 minElement() 不會總返回同樣的值。例如:
print([2, 3, 1].first)
// > Optional(2)
print([2, 3, 1].minElement())
// > Optional(1)
你可能已經(jīng)猜到了纯路,數(shù)組也有一個(gè) maxElement() 方法或油。
注意:first 和 last properties 和 minElement() 和 maxElement() 方法不只是數(shù)組有。每個(gè)集合類型都有這些 properties 和方法驰唬,在那些許許多多的屬性以外顶岸。你會在讀到協(xié)議(protocols)的時(shí)候?qū)W習(xí)更多有關(guān)這個(gè)行為的內(nèi)容腔彰。
現(xiàn)在你已經(jīng)搞明白怎么得到第一名選手了,你要介紹一下這名選手是誰:
if let currentPlayer = currentPlayer {
print("\(currentPlayer) 將要開始")}
// > Alice 將要開始
你用了 if let 來拆包從 first 得到的可選值辖佣;否則霹抛,語句會打印 Optional("Alice") 將要開始,這應(yīng)該不是你想要的吧卷谈。
這些 properties 和方法在你想得到第一個(gè)杯拐,最后一個(gè),最小的或最大的元素的時(shí)候會很有幫助世蔗。但如果你想要的元素不能通過這些 properties 或方法中的任何一個(gè)觀察到呢端逼?
使用下標(biāo)
訪問數(shù)組里的元素最方便的方式是通過使用下標(biāo)(subscript)語法。語法允許你通過使用包括索引的方括號來直接訪問任意值:
var firstPlayer = players[0]
print("第一名選手是 \(firstPlayer)")
// > 第一名選手是 "Alice"
因?yàn)閿?shù)組是零索引污淋,所以用 0 索引來獲取第一個(gè)對象顶滩。你可以使用一個(gè)更大的索引來獲得數(shù)組里的下一個(gè)元素,但如果你嘗試訪問超過了數(shù)組尺寸的索引寸爆,你會得到一個(gè)運(yùn)行時(shí)錯誤礁鲁。
var player = players[4]
// > fatal error: Array index out of range(致命錯誤:數(shù)組索引超出范圍)
為什么會得到這個(gè)錯誤呢?因?yàn)?players 只包含了四個(gè)字符串而昨。索引 4 表示第五個(gè)元素救氯,但這個(gè)數(shù)組并沒有第五個(gè)元素呀。
當(dāng)你使用下標(biāo)的時(shí)候歌憨,不需要擔(dān)心可選值,因?yàn)閲L試訪問一個(gè)不存在的索引不會返回 nil墩衙;直接就導(dǎo)致運(yùn)行時(shí)錯誤了务嫡。
使用范圍
你可以結(jié)合范圍(ranges)來使用下標(biāo)語法,從數(shù)組中獲取不止一個(gè)單獨(dú)的值漆改。例如:如果你想得到接下來要開始的兩名選手心铃,可以這樣做:
let upcomingPlayers = players[1...2]
print(upcomingPlayers)
// > ["Bob", "Cindy"]
如你所見,upcomingPlayers 常量實(shí)際上是和原始數(shù)組相同類型的數(shù)組挫剑。
你用的范圍是 1...2去扣,表示每個(gè)數(shù)組的第二和第三個(gè)項(xiàng)目。只要起始值小于結(jié)束值樊破,你可以使用任何索引愉棱,它們都在數(shù)組的范圍內(nèi)。
檢查一個(gè)元素
你可以檢查數(shù)組里是否存在至少一個(gè)特定的元素哲戚,使用 contains(_:)奔滑,如果找到了就返回 true,沒有就 false顺少。
可以用這個(gè)方法寫一個(gè)函數(shù)朋其,檢查給定的選手在不在游戲里:
func isPlayerEliminated(playerName: String) -> Bool {
if players.contains(playerName) {
return false
} else {
return true
}}
現(xiàn)在可以在任意你需要檢查選手是否被淘汰的時(shí)候使用這個(gè)函數(shù):
print(isPlayerEliminated("Bob"))
// > false
你甚至可以在一個(gè)特定范圍里測試一個(gè)元素是否存在:
players[1...3].contains("Bob")
// > true
現(xiàn)在你能從數(shù)組里取出數(shù)據(jù)了王浴,是時(shí)候看看可變數(shù)組以及如何修改它們的值了。
修改元素
你可以對可變數(shù)組做所有類型的改變:添加或移除元素梅猿,更新已存在的值氓辣,和移動元素到一個(gè)不同的順序。在這個(gè)部分袱蚓,你會看到如何處理數(shù)組钞啸,讓它配合你的游戲正在進(jìn)行的事情。
附加元素
如果新的選手想要加入游戲癞松,他們需要注冊然后添加他們的名字到數(shù)組里爽撒。Eli 是要加入已經(jīng)存在的四名選手的第一個(gè)選手。你可以使用 append(_:) 來把 Eli 加到數(shù)組的結(jié)尾:
players.append("Eli")
如果你想附加除了字符串以外的東西响蓉,編譯器會顯示一個(gè)錯誤硕勿。記住,數(shù)組只能包含相同類型的值枫甲。還有源武,append(_:) 只對可變數(shù)組奏效。
下一個(gè)加入游戲的選手是 Gina想幻。你可以用另一種方式把她附加到游戲里粱栖,使用 += 操作符:
players += ["Gina"]
這個(gè)表達(dá)式右側(cè)是只有一個(gè)單獨(dú)元素的數(shù)組,字符串"Gina"脏毯。通過使用 +=闹究,你在附加這個(gè)數(shù)組的元素到 players 上。現(xiàn)在這個(gè)數(shù)組看起來像這樣:
print(players)
// > ["Alice", "Bob", "Cindy", "Dan", "Eli", "Gina"]
這里食店,你添加一個(gè)單獨(dú)的元素到數(shù)組上渣淤,但你可以看到附加多個(gè)項(xiàng)目會多么簡單,只要在 Gina 的名字后面添加更多名字就可以了吉嫩。
插入元素
這個(gè)紙牌游戲的不成文規(guī)矩是選手的名字要按字母表順序排列价认。如你所見,這個(gè)列表遺失了一個(gè)以字母 F 開始的選手自娩。幸運(yùn)的是用踩,F(xiàn)rank 剛剛抵達(dá)。你想把他添加到列表的 Eli 和 Gina 中間忙迁。要做這件事脐彩,使用 insert(_:atIndex:):
players.insert("Frank", atIndex: 5)
atIndex 元素定義了你想添加元素的位置。記住數(shù)組是零索引的动漾,所以索引 5 實(shí)際上是在 Eli 和 Gina 中間丁屎。
移除元素
游戲進(jìn)行中,其他選手逮到了 Cindy 和 Gina 作弊旱眯。應(yīng)該把他們從游戲中移除晨川!你知道 Gina 在選手列表的最后一個(gè)证九,所以你可以輕松地使用 removeLast() 來移除她:
var removedPlayer = players.removeLast()
print("\(removedPlayer) 被移除了")
// > Gina 被移除了
這個(gè)方法做了兩件事:它移除了最后一個(gè)元素,然后返回了它共虑,以防你需要打印它或存儲到其他什么地方去——比如一個(gè)作弊者數(shù)組愧怜!
要從游戲里移除 Cindy,你需要知道她的名字被存儲的確切索引妈拌∮堤常看著選手列表,你發(fā)現(xiàn)她在列表的第三個(gè)尘分,所以她的索引是 2猜惋。
removedPlayer = players.removeAtIndex(2)
print("\(removedPlayer) 被移除了")
// > Cindy 被移除了
你會怎么獲得一個(gè)元素的索引呢?有一個(gè)方法就是做這個(gè)的培愁!indexOf(_:) 返回元素的第一個(gè)索引著摔,因?yàn)閿?shù)組可能包含同樣的值的多個(gè)拷貝。如果方法沒有找到元素定续,就返回 nil谍咆。
迷你練習(xí)
使用 indexOf(_:) 來確定元素"Dan"在 players 里的位置。
更新元素
Frank 已經(jīng)決定每個(gè)人從現(xiàn)在開始都要叫他 Franklin私股。你可以從數(shù)組里移除"Frank"值摹察,然后添加"Franklin",但那對于一個(gè)簡單的任務(wù)來說太多工作量了倡鲸。作為替代供嚎,你應(yīng)該用下標(biāo)語法來更新名字:
print(players)
// > ["Alice", "Bob", "Dan", "Eli", "Frank"]
players[4] = "Franklin"
print(players)
// > ["Alice", "Bob", "Dan", "Eli", "Franklin"]
你要小心不要用一個(gè)超出數(shù)組邊界的索引,不然你的 app 就會崩潰峭状。
隨著游戲繼續(xù)查坪,一些選手被淘汰了,新人加入替換他們宁炫。幸運(yùn)的是,你也可以結(jié)合范圍使用下標(biāo)來用一行代碼更新多個(gè)值:
players[0...1] = ["Donna", "Craig", "Brian", "Anna"]
print(players)
// > ["Donna", "Craig", "Brian", "Anna", "Dan", "Eli", "Franklin"]
這段代碼替換了前兩個(gè)選手氮凝,Alice 和 Bob羔巢,替換為新的 players 數(shù)組里的四名選手。如你所見罩阵,范圍的尺寸不需要等于你在添加的數(shù)組持有的值的尺寸竿秆。
移動元素
看看這一團(tuán)糟!players 數(shù)組包含名字的首字母從 A 到 F稿壁,但他們的順序不對幽钢,這違背了游戲的規(guī)則。
你可以嘗試解決這個(gè)狀況傅是,通過手動一個(gè)一個(gè)移動值到正確的位置上匪燕,像這樣:
let playerAnna = players.removeAtIndex(3)
players.insert(playerAnna, atIndex: 0)
print(players)
// > ["Anna", "Donna", "Craig", "Brian", "Dan", "Eli", "Franklin"]
如果你想移動一個(gè)單獨(dú)的元素蕾羊,這會奏效,但如果你想排序整個(gè)數(shù)組帽驯,應(yīng)該使用sort():
players = players.sort()
print(players)
// > ["Anna", "Brian", "Craig", "Dan", "Donna", "Eli", "Franklin"]
sort() 做的完全就是你預(yù)想的那樣:返回一個(gè)數(shù)組排序好的拷貝龟再。如果您想對數(shù)組本身(in place)進(jìn)行排序,而不是返回一個(gè)已排序的副本尼变,應(yīng)該用 sortInPlace()利凑。
遍歷數(shù)組
現(xiàn)在時(shí)間越來越晚了,選手決定今晚暫停嫌术,明天繼續(xù)哀澈;在此期間,你要把他們的分?jǐn)?shù)保存到一個(gè)單獨(dú)的數(shù)組里度气。你會在關(guān)于字典的教程里看到更好的方式來做這個(gè)割按,但現(xiàn)在你要繼續(xù)用數(shù)組:
let scores = [2, 2, 8, 6, 1, 2]
選手離開之前,你想打印仍然在游戲里的名字蚯嫌。你可以使用 for-in 循環(huán)來做哲虾,你在第6篇教程里已經(jīng)讀到。
for playerName in players {
print(playerName)
}
// > Anna
// > Brian
// > Craig
// > Donna
// > Eli
// > Franklin
這段代碼經(jīng)過了 players 的所有元素择示,從 0 索引知道 players.count - 1束凑,打印了它們的值。在第一個(gè)循環(huán)里栅盲,playerName 等于數(shù)組的第一個(gè)元素汪诉;第二個(gè)循環(huán)里,等于數(shù)組的第二個(gè)元素谈秫;以此類推直到循環(huán)已經(jīng)打印了數(shù)組里的所有名字扒寄。
如果你也需要每個(gè)元素的索引,你可以遍歷數(shù)組的 enumerate() 方法的返回值拟烫,返回一個(gè)元祖包含數(shù)組里每個(gè)元素的索引和值该编。
for (index, playerName) in players.enumerate() {
print("\(index + 1). \(playerName)")
}
// > 1. Anna
// > 2. Brian
// > 3. Craig
// > 4. Donna
// > 5. Eli
// > 6. Franklin
現(xiàn)在你可以這個(gè)剛剛學(xué)到的技術(shù)來寫一個(gè)函數(shù),帶有一個(gè)整數(shù)的數(shù)組作為它的輸入硕淑,返回它的元素的和:
func sumOfAllItems(intArray: [Int]) -> Int {
var sum = 0
for number in intArray {
sum += number
}
return sum
}
你可以用這個(gè)函數(shù)來計(jì)算選手的總分:
print(sumOfAllItems(scores))
// > 21
迷你練習(xí)
寫一個(gè) for-in 循環(huán)课竣,打印選手的名字和分?jǐn)?shù)。
順序操作
你剛剛看到的 for-in 循環(huán)都有一個(gè)共同點(diǎn):他們遍歷了數(shù)組的所有元素置媳,然后對每個(gè)項(xiàng)目實(shí)施了特定的行為于樟。
Reduce
reduce(_:combine:) 帶有一個(gè)初始值作為第一個(gè)參數(shù),然后按順序累計(jì)數(shù)組里的每個(gè)值拇囊,使用 combine 操作迂曲。一個(gè)例子勝過千言萬語:
let sum = scores.reduce(0, combine: +)
print(sum)// > 21
這和寫 let sum = 0 + scores[0] + scores[1] + ... +scores[5] 是相同的。它做了 sumOfAllValues(_:) 做的完全一樣的事寥袭,但只有一行代碼路捧。
combine 參數(shù)是很高端的关霸,它支持不止一個(gè)單獨(dú)的算術(shù)操作。它是一個(gè)閉包(closure)鬓长,你會在后面的教程里學(xué)習(xí)更多有關(guān)內(nèi)容谒拴,“函數(shù)式編程∩娌ǎ”
Filter
filter(:) 返回一個(gè)新數(shù)組英上,通過從它被調(diào)用的數(shù)組中篩選出元素。它唯一的參數(shù)是一個(gè)閉包啤覆,返回一個(gè)布爾型苍日,它會為數(shù)組里的每個(gè)元素執(zhí)行這個(gè)閉包一次。如果閉包返回 true窗声,filter(:) 會添加元素到返回的數(shù)組中相恃;否則它會忽略這個(gè)元素。
例如笨觅,在你的紙牌游戲里柠掂,你想打印這天的高分凯砍,你把這定義為大于 5 分的分?jǐn)?shù):
print(scores.filter({ $0 > 5 }))
// > [8, 6]
你可以使用簡寫的 $0 指向閉包里的第一個(gè)參數(shù)蛆封,這樣你就能指向 filter(_:) 當(dāng)前在操作的元素喻杈。
Map
map(_:) 也帶有唯一的閉包參數(shù)。就像它名字表示的苍苞,它將一個(gè)數(shù)組中的每個(gè)值映射(map)到一個(gè)新值固翰,使用閉包參數(shù)。
再一次羹呵,來個(gè)例子會更易懂骂际。假設(shè)你感覺很慷慨,你想要把每個(gè)選手擁有的值翻倍冈欢。
print(scores)
// > [2, 2, 8, 6, 1, 2]
let newScores = scores.map({ $0 * 2 })
print(newScores)
只要一行代碼歉铝,你已經(jīng)創(chuàng)建了一個(gè)新數(shù)組,分?jǐn)?shù)都被乘以 2凑耻。
注意:你會在后面的教程學(xué)習(xí)更多有關(guān)順序操作犯戏,“函數(shù)式編程∪埃”
數(shù)組操作的運(yùn)行時(shí)間
數(shù)組在內(nèi)容里存儲為連續(xù)不斷的塊。意味著如果你在一個(gè)數(shù)組里有十個(gè)元素种吸,十個(gè)值都被一個(gè)接一個(gè)存儲弃衍。記在心中,這是多個(gè)數(shù)組操作消耗的表現(xiàn):
訪問元素:獲取一個(gè)元素的消耗是 O(1)坚俗。因?yàn)樗兄刀际切蛄谢木刀ⅲ?em>隨機(jī)訪問和獲取位于特定索引的值很簡單岸裙;編譯器要知道的就是數(shù)組從哪里開始,以及你想要獲取哪個(gè)索引速缆。
插入元素:添加一個(gè)元素的復(fù)雜性基于你添加新元素的位置在哪:
- 如果你在數(shù)組的開頭添加降允,Swift 可以用 O(1) 完成。
- 如果你添加到數(shù)組的中間艺糜,從這個(gè)索引開始的所有值都需要被平移剧董。這樣做會需要 N/2 操作,因此運(yùn)行時(shí)間是 O(N)破停。
- 如果你添加到數(shù)組的末尾并且有空間的話翅楼,它會使用 O(1)。如果沒有空間真慢,Swift 需要從其他什么地方找到空間毅臊,在添加之前復(fù)制整個(gè)數(shù)組過去,會占用 O(N)黑界。但平均情況是 O(1)管嬉,因?yàn)閿?shù)組大部分情況下都不是滿的。
刪除元素:刪除一個(gè)元素留下了被移除的元素本來所在的空當(dāng)朗鸠。就像之前提到的蚯撩,數(shù)組里的所有元素需要被序列化這樣空當(dāng)才能關(guān)閉,通過向前平移元素童社。
復(fù)雜性和插入元素相似:如果你在移除開頭或結(jié)尾的元素求厕,這是一個(gè) O(1) 操作。如果你在從中間移除扰楼,復(fù)雜性是 O(N)呀癣。
搜索一個(gè)元素:如果你在搜索的元素是數(shù)組的第一個(gè)元素,搜索會在一次操作后結(jié)束弦赖。如果這個(gè)元素不存在项栏,你需要執(zhí)行 N 次操作直到你意識到元素沒有被找到。平均來看蹬竖,搜索一個(gè)元素會用 N/2 操作沼沈,因?yàn)樗阉饔幸粋€(gè) O(N) 的復(fù)雜性。
當(dāng)你在閱讀接下來的關(guān)于數(shù)組和集合的教程的時(shí)候币厕,你會看到他們的表現(xiàn)特征和數(shù)組有什么不同列另。這會給你一點(diǎn)靈感,對于特定情況應(yīng)該用哪個(gè)集合類型旦装。
關(guān)鍵點(diǎn)
- 數(shù)組(Arrays)是相同類型的值的有序集合页衙。
- 使用下標(biāo)(subscripting)來訪問和更新元素。
- 數(shù)組是一個(gè)值類型,所以被分配到一個(gè)新的變量或作為參數(shù)傳遞給函數(shù)的時(shí)候是被拷貝的店乐。
- 千萬不要訪問一個(gè)越界索引艰躺。
接下來去哪兒?
數(shù)組在編程里很常見眨八,當(dāng)你需要操作許多特定類型的元素的時(shí)候你會看到他們被大量使用腺兴。
在接下來的兩篇教程里,你會學(xué)習(xí)字典(dictionaries)和集合(sets)廉侧,另外兩個(gè) Swift 內(nèi)置的集合類型页响。當(dāng)你繼續(xù)的時(shí)候,仍然把數(shù)組放在腦海里伏穆,這樣就可以比較和發(fā)現(xiàn)差異拘泞;這會讓你看到區(qū)別,幫助你判斷何時(shí)使用哪個(gè)集合類型枕扫。
挑戰(zhàn)
挑戰(zhàn) A:你就是編譯器
以下哪個(gè)是有效語句陪腌?
1. let array1 = [Int]()
2. let array2 = []
3. let array3: [String] = []
對于接下來的5條語句,array4 被如此聲明:
let array4 = [1, 2, 3]
4. print(array4[0])
5. print(array4[5])
6. array4[1...2]
7. array4[0] = 4
8. array4.append(4)
對于最后的5條語句烟瞧,array5 被如此聲明:
var array5 = [1, 2, 3]
9. array5[0] = array5[1]
10. array5[0..1] = [4, 5]
11. array5[0] = "Six"
12. array5 += 6
13. for item in array5 { print(item) }
挑戰(zhàn) B:移除一個(gè)元素
寫一個(gè)函數(shù)诗鸭,移除一個(gè)整數(shù)數(shù)組中給定的整數(shù)的第一個(gè)存在。這是函數(shù)的結(jié)構(gòu):
func removeOnce(itemToRemove: Int, fromArray: [Int]) -> [Int]
挑戰(zhàn) C:挑選
寫一個(gè)函數(shù)從整數(shù)數(shù)組中移除給定的整數(shù)的所有存在参滴。這是函數(shù)的結(jié)構(gòu):
func remove(itemToRemove: Int, fromArray: [Int]) -> [Int]
挑戰(zhàn) D:反轉(zhuǎn)數(shù)組
數(shù)組有一個(gè) reverse() 方法强岸,反轉(zhuǎn)數(shù)組里項(xiàng)目的順序。寫一個(gè)函數(shù)反轉(zhuǎn)數(shù)組砾赔,但不使用 reverse()蝌箍。這是函數(shù)的結(jié)構(gòu):
func reverse(array: [Int]) -> [Int]
挑戰(zhàn) E:隨機(jī)化數(shù)組
下面的函數(shù)返回一個(gè)介于 0 和給定參數(shù)之間的隨機(jī)數(shù)字:
import Foundation
func randomFromZeroTo(number: Int) -> Int {
return Int(arc4random_uniform(UInt32(number)))
}
使用它寫一個(gè)函數(shù),把數(shù)組里的元素打亂成為隨機(jī)順序暴心。這是函數(shù)的結(jié)構(gòu):
func randomArray(array: [Int]) -> [Int]
挑戰(zhàn)源代碼
https://yunpan.cn/cMZGvcJpQDZqD (提取碼:f737)
介紹
歡迎來到Swift世界妓盲!Swift是一門蘋果在2014年夏天發(fā)布的編程語言。從那之后专普,Swift發(fā)布了一個(gè)主要的版本跳躍悯衬,成為了開始在蘋果平臺:iOS,OS X檀夹,watchOS和tvOS開發(fā)的最簡單的方式筋粗。
誰適合這篇教程
這篇教程適合懂一點(diǎn)編程、并且希望學(xué)習(xí)Swift的人炸渡。也許你已經(jīng)為網(wǎng)站寫過一些JavaScript代碼娜亿,或者用Python寫過一些簡短的程序。這篇教程就是為你準(zhǔn)備的蚌堵!你會學(xué)習(xí)到編程的基本概念暇唾,同時(shí)也會成為Swift語言小能手。
如果你是赤裸裸的編程新手,這篇教程也是為你準(zhǔn)備的策州!教程里貫穿有簡短的鍛煉和挑戰(zhàn)來給你一些編程練習(xí),同時(shí)測試你的知識宫仗。
需要準(zhǔn)備什么
要看這篇教程够挂,你需要準(zhǔn)備如下的東西:
- 一臺運(yùn)行OS X El Captian(10.11)的Mac,帶有最新發(fā)布的更新并且安裝了安全補(bǔ)丁藕夫。這樣你才能夠安裝需要的開發(fā)工具:最新版本的Xcode孽糖。
- Xcode 7.3 或更新的版本。Xcode是用Swift寫代碼的主要開發(fā)工具毅贮。最小也需要Xcode 7.3版本办悟,因?yàn)槟莻€(gè)版本包含Swift 2.2。你可以免費(fèi)從Mac App Store下載Xcode的最新版本滩褥,這里:http://apple.co/1FLn51R病蛉。
如果你還沒有安裝Xcode最新版本,在繼續(xù)看下面的教程前要確定安裝瑰煎。
如何使用這篇教程
每篇教程都會介紹觸手可及的話題理論铺然,伴隨大量Swift代碼來示范在學(xué)習(xí)的實(shí)際的應(yīng)用程序。
教程里的所有代碼都是平臺中立的酒甸;這意味著不是為iOS魄健、OS X或任何其它平臺而特定。代碼在playgrounds里運(yùn)行插勤,你在本篇中已經(jīng)學(xué)習(xí)了沽瘦。
在剩下的教程里,你可以把代碼在自己的playground里輸入進(jìn)去农尖。這樣你就可以和代碼“玩宋隽担”(play around),做一些改變立即就能看見代碼運(yùn)行的結(jié)果卤橄。
剩下的教程里會貫穿實(shí)際小練習(xí)绿满,都是簡短的練習(xí),關(guān)于觸手可及的主題窟扑。每篇的末尾也有挑戰(zhàn)喇颁,會有編程問題也會有長一點(diǎn)的代碼練習(xí)來測試你的知識。做完就能掌握大部分的Swift基礎(chǔ)知識嚎货。