Swift學習筆記--為代碼的執(zhí)行做個決定

為代碼的執(zhí)行做個決定

[TOC]

和其他的編程語言一樣,為了能夠控制程序的執(zhí)行路徑,Swift提供了我們熟悉的循環(huán)和分之判斷語句。首先我們先快速的過一遍他們的基本用法。

條件分支語句

第一個要介紹的,是if...else if...else...。這是幾乎每種語言都支持的分支表達方式,其中else ifelse都是可選的部分,它們可以單獨和if搭配形式各種分支條件的判斷?;旧?,看到代碼,我們就可以直接了解這類判斷的含義了。

var light = "red"
var action = ""

if light == "red" {
    action = "stop"
}
else if light == "yellow" {
    action = "caution"
}
else if light == "green" {
    action = "go"
}
else {
    action = "invalid"
}

在上面這個紅綠燈的代碼里,我們不斷根據light的值,設置了變量action的值,它很簡單。但通常,我們還是更多會使用if...else...表示非黑即白這樣的簡單關系。

對于上面這種存在多種可能性的情況,在Swift里,我們通常還是會使用switch...case...來表示,它比if...else...更安全,也更有更好的表意:

switch light {
    case "red":
        action = "stop"
    case "yellow":
        action = "caution"
    case "green":
        action = "go"
    default:
        action = "invalid"
}

這里,我們使用switch...case...表達了和之前的if...else...相同的語義。但是,它更明確的表達了當light的值(switch)為各種情況(case)時,我們應該采取哪些措施,這樣的概念。

但和C++/Java這樣語言相比,Swift中的switch...case...也有一些自己獨特的地方:

首先,case語句必須exhausitive,也就是說,必須覆蓋switch后面出現(xiàn)的表達式的所有情況,否則會導致編譯錯誤.

當你不需要對列出case的其他情況作出處理時,你也要在default分支寫上一句break,明確表示你考慮到了其他的情況,只是你不需要更多額外處理而已。

其次,每個case語句不會自動“貫通”到下一個case,因此我們也無需在每個case最后一行寫break表示結束;

最后,當我們要在一個case里匹配多個條件的時候,可以使用逗號把多個條件分開,以上,就是和分支條件相關的兩個最基本的場景和用法,接下來,我們了解循環(huán)。

循環(huán)控制語句

第一個要介紹的,是for element in collection/range,我們可以用它來方便的遍歷一個集合類型或者范圍:

let vowel = ["a", "e", "i", "o", "u"]

for char in vowel {
    print(char)
}
// aeiou

for number in 1...10 {
    print(number)
}
// 12345678910

第二個循環(huán)的方式是while,它有前置判斷和后置判斷兩種形式,基本上保留了原汁原味的C用法:

// while
var i  = 0
while i < 10 {
    print(i)
    i += 1
}

// do ... while
repeat {
    print(i)
    i -= 1
} while i > 0

在這兩類循環(huán)里,我們都可以用continue來停止執(zhí)行當前循環(huán)中的語句,立即開始下一次循環(huán)。例如,打印所有的偶數(shù):

for number in 1...10 {
    if number % 2 != 0 { continue }
    print(number)
}
// 2 4 6 8 10

在這個例子里,如果number是奇數(shù),就會執(zhí)行到continue,當前循環(huán)就停止并自動進入下一次循環(huán)了。

或者,我們也可以使用break來終止整個循環(huán)。例如,值大于8時,就終止循環(huán):

for number in 1...10 {
    if number > 8 { break }
    print(number)
}
// 1 2 3 4 5 6 7 8

使用簡單的樣式匹配

現(xiàn)實環(huán)境中,我們需要的判斷條件可能比那些演示的例子復雜的多。為此,Swift從函數(shù)式編程中借鑒了一些樣式匹配的方式,幫助我們構建表意豐富又易于維護的代碼。

匹配值的方式

為了演示各種樣式匹配的方式,我們先定義一個tuple,表示平面直角坐標系中的原點:

let origin = (x: 0, y: 0)

當我們要判斷某個點是否是原點的時候,最原始的方式,是這樣的:

let pt1 = (x: 0, y: 0)
if pt1.x == 0 && pt1.y == 0 {
    print("@Origin")
}

當然,這樣判斷xy坐標是否相等并不能讓人滿意,寫起來非常麻煩。實際上,我們還可以這樣:

if case (0, 0) = pt1 {
    print("@Origin")
}

我們可以用case 匹配的值 = 要檢查的對象的方式,對要檢查的對象進行判斷。在我們的例子里,判斷的就是pt1是否等于原點。

除了用在if中匹配值,我們當然也可以在switch的case分支里,匹配特定形式的值:

switch pt1 {
case (0, 0):
    print("@Origin")
case (_, 0):
    print("on x axis")
case (0, _):
    print("on y axis")
case (-1...1, -1...1):
    print("inside 2x2 square")
default:
    break;
}

在上面這個例子里,除了用case (0, 0)表示匹配原點值之外,還可以用(_, 0)(0, _)表示忽略掉_的部分,僅對tuple中某一部分的值進行匹配,或者,在tuple的每一個成員位置,使用range operator匹配值的某個范圍。

除了把case用于條件分支語句,我們還可以用于循環(huán)語句,用于進一步控制循環(huán)條件,例如:

let array1 = [1,1,2,2,2]

for case 2 in array1 {
    print("found two")
}

在上面這個例子里,當遇到數(shù)組中值為2的元素時,我們向控制臺打印了一行話,因此,print一共會打印3次。

把匹配的內容綁定(value binding)到變量

除了在 case中使用各種形式的具體值之外,我們還可以把匹配到的內容直接綁定到變量上,這樣我們就可以再相應的處理代碼中直接使用它們,例如:

switch pt1 {
    case (let x, 0):
        print("(\(x),0) is on x axis")
    case (0, let y):
        print("(0,\(y)) is on y axis")
    default:
        break;
}

在上面這個例子里,我們把之前_的部分換成了let xlet y,這樣,同樣是匹配在坐標軸上的點,這次,我們就可以在對應的case中,直接訪問匹配到的值了。我們管這樣的形式,叫做value binding。

除了直接綁定變量自身的值之外,我們還可以用類似的形式綁定enum中的關聯(lián)值。例如,我們先定義一個表示方向的enum

enum Direction {
    case north, south, east, west(abbr: String)
}

let west = Direction.west(abbr: "W")

為了演示,我們給.west添加個了一個associated value,表示方向的縮寫。然后,我們既可以像這樣來判斷enum值自身:

if case .west = west {
    print(west) // west("W")
}

此時,print打印的就是enum case的值。我們也可以這樣來直接綁定westassociated value

if case .west(let direction) = west {
    print(direction) //W
}

此時,print打印出來的值,就直接是字符“W”了。當然,case這樣的用法,在switch的分支中,也是完全可以的。

自動提取optional的值

除了綁定enum的associated value之外,我們還可以使用case來自動提取optional類型的非空值:

let skill: [String?] = ["Swift", nil,"PHP","JavaScript",nil]

for case let skill? in skills {
    print(skill) // Swift PHP JavaScript
}

在我們的例子里,skills包含了5個元素,其中兩個是nil,當我們用case let skill?這樣的形式來綁定optional值的時候,Swift就會自動提取每一個非nil的元素,因此,print會輸出“Swift PHP JavaScript”。

自動綁定類型轉換的結果

最后一類基本的樣式匹配規(guī)則是自動綁定類型轉換的結果。首先,我們創(chuàng)建一個[Any]

let someValue: [Any] = [1, 1.0, "One"]

當我們遍歷someValues,并且要根據不同類型的數(shù)組元素分別做一些操作的時候,可以這樣:

for value in someValues {
    switch value {
    case let v as Int:
        print(Interger \(v))
    case let v as Double:
        print(Double \(v))
    case let v as String:
        print(String \(v))
    default:
        print("Invalid value")
    }
}
// Integer 1
// Double 1.0
// String One

在上面的例子中,我們使用了case let Variable as Type的方式,把類型轉換成功的結果,綁定在了變量V上。這樣,我們就可以在對應的case里,訪問到轉換成功的值了。

或者,如果你僅僅想判斷類型,而不需要知道具體內容的話,還可以使用更簡單的is操作符:

for value in someValues {
    switch value {
    case is Int:
        print("Integer")
    // omit for simplicity...
}

使用高級樣式匹配方式

使用where約束條件

除了使用具體的數(shù)值對循環(huán)或分支條件進行約束外,我們還可以使用where進行更復雜的約束。先來看一個簡單的例子:

for i in 1...10 where i % 2 == 0 {
    print(i)
}

這里我們在for循環(huán)里使用了where限定了進入循環(huán)的值必須是偶數(shù)。我們還可以把where用在更復雜的value binding語句里。例如,我們假設定義下面的enum表示手機電量

enum Power {
    case fullyCharges
    case normal(percentage: Double)
    case outOfPower
}

然后在定義一個battery表示手機電池

let battery = Power.normal(percentage: 0.1)

這樣,我們就可以在綁定.normalassociated value的同時,使用where進一步約束它的關聯(lián)值:

switch battery {
    case .normal(let precentage) where percentage <= 0.1":
        print("almost out of power")
    case .normal(let percentage) where percentage >= 0.8:
        print("almost fully power")
    case .fullyCharges, .outOfPower:
        print("Fully charged or out of power")
    default:
        break
}

上面的case語句中,batteryfullyChargesoutOfPower我們使用逗號分隔,表示邏輯或的概念,逗號也可以用在if中表示邏輯與的概念,例如為了處理之前電量低的情況,我們還可以用if這樣來實現(xiàn):

if case .normal(let percentage) == battery, case 0...0.1 = percentage {
    print("Almost out of power")    
}

在上面的代碼里,第一個if case使用value binding讀取了battery中.normal的associated value。接下來,第二個case進一步約束了第一個case中關聯(lián)到的值小于10%的情況。

使用tuple簡化多個條件的比較

有時,我們需要在if中同時比較多個條件。假設,我們有一對用戶名和密碼:

let username = "11@boxue.io"
let password = 11111111

當我們要同時比較這兩個值時,最普通的寫法,就是在if中使用邏輯與(&&)操作符:

if username == "11@boxue.io" && password == 11111111 {
    print("correct")
}

如果你不喜歡在if中并列多個比較語句,我們還可以用case臨時生成一個tuple,并且,讓它和username / password進行比較:

if case ("11@boxue.io", 11111111) = (username,password) {
    print("correct")
}

理解樣式匹配的實現(xiàn)方式

首先,當要匹配的變量和樣式的類型相同,并且對應的類型實現(xiàn)了Equatable protocol時,就直接使用對應類型的==操作符進行匹配。例如我們之前比較用戶名和密碼的例子:

let username = "11@boxue.io"
let password = 11111111

if case ("11@boxue.io", 11111111) = (username, password) {
    print("correct")
}

這里,就直接使用了Tuple類型的比較操作符。

其次,如果樣式和要匹配的變量類型不同,或對應類型沒有實現(xiàn)Equaltable protocol時,Swift會使用~=操作符進行比較。當這個操作符返回true時,就認為條件匹配,否則就認為不匹配。因此,為了判斷某個數(shù)值是否在某個范圍里,Swift標準庫中實現(xiàn)了Range ~= Value這種形式的比較,但是,卻沒有實現(xiàn)Value ~= Range這樣的版本。

因此,當我們寫成case percentage = 0...0.1時,編譯器就會因為找不到對應的操作符而報錯了。不過,由于這個操作符是可以自定義的,因此,我們可以通過重載它,來實現(xiàn)上面的功能:

func ~=<T>(value: T, pattern: ClosedRange<T>) -> Bool {
        return pattern.contains(value)
}

為了能匹配不同的類型,我們把~=定義為了一個泛型函數(shù)。它的第一個參數(shù)表示~=的左操作數(shù),按照我們的例子,應該是一個值;第二個參數(shù)表示~=的右操作數(shù),它是一個ClosedRange<T>。在它的實現(xiàn)里,我們只要調用ClosedRangecontains方法,就可以實現(xiàn)“是否包含某個值”這樣的語義了。

而當我們重載了這個~=方法之后,之前發(fā)生編譯錯誤的語句就可以正常工作了。我們也可以通過類似的方法,為自定義類型添加各種樣式匹配規(guī)則。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,829評論 18 139
  • 本章將會介紹 控制流For-In 循環(huán)While 循環(huán)If 條件語句Switch 語句控制轉移語句 continu...
    寒橋閱讀 737評論 0 0
  • 組卷網,高考資源網,中學學科網,菁優(yōu)網我要自學網。
    ff33440ed08d閱讀 337評論 0 1
  • 這條文章教大家判斷零售行業(yè)到底是該走線上還是走線下。 在零售行業(yè),很多人說線下才有機會,連馬云都說未來純電商的日子...
    左右文摘閱讀 950評論 0 1
  • 涼拖破了一條長長的口子,突然想起了小時后焊涼鞋的事情。 小時后的涼鞋,男孩穿的主要是灰色塑料制成,不知道什么材質,...
    liyongthethird閱讀 656評論 0 0