運算子(operator)是用來檢查,改變或合併值的一種特殊符號或短語。例如,加號運算子讓兩個數字相加(如:let i = 1 + 2),還有些更復雜的運算子,如邏輯與運算子(&&)(如:if enteredDoorCode && passedRetinaScan)和自增運算子(++i)(將 i 的值加 1 的便捷寫法)。
Swift 支援標準C語言的大多數運算子,並且改進了一些特性以規避常見的程式碼錯誤。賦值運算子(=)是沒有返回值的,這樣是為了避免在使用等於運算子(==)時誤用了賦值運算子(=)。算術運算子(+,-,*,/,% 等等)會偵測並阻止值溢位,可避免在運算時超出儲存型別的值域範圍(比實際運算結果大或小,或不精準--Joe.Huang)。如果需要支援溢位行為,可以選用 Swift 的溢位運算子,詳情可見 溢位運算子(後面章節譯到)。
與C語言不同的是,Swift 允許對浮點數求餘(%)。Swift 還提供了C語言所沒有的兩個區間運算子(a..b和a...b),作為表示值域範圍的便捷寫法。
本章介紹 Swift 中的常用運算子。高階運算子 (後面章節譯到) 一章涵蓋了 Swift 中的高階運算子,並講解了如何自定義運算子,以及讓標準運算子支援自定義型別的運算。
運算子術語
運算子分為一元,二元,三元運算子:
· 一元運算子(unary operator)對單個目標進行運算(如 -a)。一元運算子字首(unary prefix operator)緊跟運算目標之前(如 !b),而一元運算子字尾(unary postfix operator)則緊跟運算目標之後(如 i++)。
· 二元運算子(binary operator)對兩個目標進行運算(如 2 + 3),它們屬於中綴(infix)運算子,因為(運算子號)出現在兩個運算目標之間。
· 三元運算子(ternary operator)對三個目標進行運算。與 C 語言一樣,Swift 只有一個三元運算子:三元條件運算子(即 a ? b : c)。
運算子操作的值稱為運算元(operands)。在表示式 1 + 2 中,+ 符號是二元運算子,它的兩個運算元為值 1與值 2。
賦值運算子
賦值運算子(assignment operator,a = b)用 b 的值初始化或更新 a 的值:
1 let b = 10 2 var a = 5 3 a = b 4 // a is now equal to 10 5 //a現在等於10
如果賦值語句的右側是一個包含多個值的元組,元組的元素可一次性用多個常量或變數分解提取出來(上一章講解元組時提到過分解元組值的方法--Joe.Huang):
1 let (x, y) = (1, 2) 2 // x is equal to 1, and y is equal to 2 3 //x等於1,y等於2
與 C 或 Objective-C 語言的賦值運算子不同,Swift 語言的賦值運算子本身沒有返回值。因此下面的語句不正確:
1 if x = y { 2 // this is not valid, because x = y does not return a value 3 // 這是無效的,因為 x = y 不會返回一個值 (x==y才可以,--Joe.Huang) 4 }
該特性可避免在使用等於運算子(==)時誤用了賦值運算子(=)。通過否認 if x = y 的有效性,Swift 將幫助你規避程式碼中出現這樣的錯誤。
算術運算子
Swift支援對所有數字型別使用四種標準算術運算子:
· 加:+
· 減:-
· 乘:*
· 除:/
1 1 + 2 // 等於3 2 5 - 3 // 等於2 3 2 * 3 // 等於6 4 10.0 / 2.5 // 等於4.0
與 C / Objective-C 語言的算術運算子不同,Swift 的算術運算子預設不允許值溢位。如果需要支援溢位行為,可以選用 Swift 的溢位運算子(如,a &+ b),詳情可見 溢位運算子(後面章節譯到)。
加號運算子也支援 String 拼接:
"hello, " + "world" // 等於 "hello, world"
可以將兩個 Character (字元,Unicode字元--Joe.Huang)值相加,或將一個 Character 值與一個 String 值相加,得到新的 String 值:
1 let dog: Character = "" 2 let cow: Character = "" 3 let dogCow = dog + cow 4 // dogCow 等於 ""
詳見 字元與字串拼接 (後面章節譯到):
求餘運算子
求餘運算子(remainder operator,a % b)求出 a 包含多少個 b,並返回剩餘的值(即整除後的餘數 remainder)。
注:
求餘運算子(%)在其他語言中也稱作求模運算子(modulo operator)。但對負數的運算結果表明:Swift 語言的實現是嚴格的求餘操作,而非求模操作。
求餘運算子的原理如下。 要計算 9 % 4,首先要求出 9 裡面包含多少個 4:
如圖所示,9 裡面包含兩個 4,餘數是 1 (橙色部分)。
Swift中的寫法如下:
9 % 4 //等於 1
要求出 a % b 的結果,% 運算子會計算下面的等式,並將餘數作為輸出結果返回:
a = (b × some multiplier) + remainder
其中 some multiplier 是 a 中能裝下 b 的最大數目。
把 9 和 4 代入等式:
9 = (4 × 2) + 1
a 為負數時,求餘方法不變:
-9 % 4 // 等於 -1
把 -9 和 4 代入等式:
-9 = (4 × -2) + -1
得到的餘數值為-1。
b 為負值(-b)時,b 的負號將被忽略。因此 a % b 與 a % -b 總是返回相同的結果。
浮點數的求餘計算
與 C / Objective-C 語言的餘數運算子不同,Swift 的餘數運算子還能對浮點數進行求餘計算:
8 % 2.5 // equals 0.5
上例中,8 除以 2.5 等於 3,餘數為 0.5,因此餘數運算子返回 Double 型的值 0.5。
自增與自減運算子
與 C 語言類似,Swift 也提供了自增運算子(++)與自減運算子(--),作為將數字變數的值加上或減去 1 的便捷寫法。任何整型或浮點型的變數都可以使用這兩個運算子。
1 var i = 0 2 ++i // i 現在等於 1
每次呼叫 ++i 時,i 的值就會增加 1。本質上,++i 就是 i = i + 1 的便捷寫法。類似地,--i 也相當於 i = i - 1。
++ 與 -- 兩對符號可以作為字首或字尾運算子使用。++i 與 i++ 均可用來將 i 的值加 1。類似地,--i 與 i-- 均可用來將 i 的值減去 1。
注意,這些運算子不僅會改變 i 的值,還會返回一個值。如果你只需要將自增或自減後的值存放在 i 中,那你可以忽略運算子的返回值。但如果你確實要用到返回值,要注意字首及字尾運算子返回值的差異,規則如下:
· 如果運算子在變數名前面,先改變它的值,再返回其值。
· 如果運算子在變數名後面,先返回原值,再改變它的值。
如例:
1 var a = 0 2 let b = ++a 3 // a 和 b 現在都等於 1,即改變a的值,再返回 4 let c = a++ 5 // a 現在等於 2, 但 c 還是自增前的值 1,即先返回的a的原值,再改變其值
在上例中,let b = ++a 先增加 a 的值(加1),然後才返回它的值。因此 a 與 b 都等於新的值 1。
但是,let c = a++ 先返回 a 的原值(加1之前的值),然後才增加 a 的值。即 c 得到了原值 1,然後 a 被更新為新值 2。
除非你需要利用 i++ 的這一特性,建議你在所有情況下都使用 ++i 與 --i,因為它們先修改 i 再返回修改後的值的動作更符合邏輯。
一元減號運算子
數值前加上字首 - ,這個字首運算子 - 就稱為一元減號運算子:
1 let three = 3 2 let minusThree = -three // minusThree 等於 -3 3 let plusThree = -minusThree // plusThree 等於 3, 或等於 "減去 minusThree"
一元減號運算子(-)緊靠所要操作的值之前,無需任何空格。
一元加號運算子
一元加號運算子(+)直接返回所操作的值,不作任何處理:
1 let minusSix = -6 2 let alsoMinusSix = +minusSix // alsoMinusSix 等於 -6
儘管一元加號運算子實際上不作任何運算,程式碼中仍然可以用它(提供語義資訊,一元減號運算子表示負數,一元加號運算子表示正數--Joe.Huang)與表示負數的一元減號運算子形成對比。
複合賦值運算子
與 C 語言類似,Swift 也提供複合賦值運算子(compound assignment operator)將賦值運算子(=)與其他運算組合起來使用。例如加法賦值運算子(addition assignment operator,+=):
1 var a = 1 2 a += 2 3 // a 等於 3
上例中的表示式 a = a + 2 簡寫為 a += 2,加法和賦值兩項操作組合為一個單項操作,執行時非常高效。
注:
複合賦值操作沒有返回值,即,你不能寫:let b = a += 2,這一操作與前面所提到的自增自減操作是不同的。
複合賦值運算子的完整列表可在 表示式 一章中找到(後面章節譯到)。
比較運算子
Swift支援標準C 的比較運算子:
等於 (a == b)
不等於 (a != b)
大於 (a > b)
小於 (a < b)
大於等於 (a >= b)
小於等於 (a <= b)注:
Swift還提供了恆等(===)和不恆等(!==)兩個鑑別運算子,你可以用它測試兩個物件是否引用了同一個物件例項。更多詳情請參考 類和結構 一章(後面章節譯到)。
每個比較運算子都會返回一個 Bool 值,檢測表示式是否成立:
1 1 == 1 // true, 因為 1 等於 1 2 2 != 1 // true, 因為 2 不等於 1 3 2 > 1 // true, 因為 2 大於 1 4 1 < 2 // true, 因為 1 小於 2 5 1 >= 1 // true, 因為 1 大於等於 1 6 2 <= 1 // false, 因為 2 大於等於 1
比較運算子常見於條件表示式中,如 if 條件句:
1 let name = "world" 2 if name == "world" { 3 println("hello, world") 4 } else { 5 println("I'm sorry \(name), but I don't recognize you") 6 //輸出(”對不起\name,我不認識你“) 7 } 8 // prints "hello, world", because name is indeed equal to "world" 9 //輸出 "hello world",因為 name 確實等於"world"
if 語句的更多介紹,詳見 流程控制 一章(後面章節譯到)。
三元條件運算子
三元運算子是一種特殊運算子,由三個部分組成,表現形式為:question ? answer1 : answer2。它是一種求值簡寫:根據 question 是否成立,從兩個表示式中取出一個並求值。
如果 question 成立,則計算 answer1 的結果並返回其值;否則計算 answer2 並返回其值。
三元條件運算子是如下程式碼的縮寫:
1 if question { 2 answer1 3 } else { 4 answer2 5 }
下面的例子將計算表格某行的畫素高度。如果該行有表頭,則行高應比內容高度高 50 個畫素;如果沒有表頭,則只高出 20 個畫素:
1 let contentHeight = 40 2 let hasHeader = true 3 let rowHeight = contentHeight + (hasHeader ? 50 : 20) 4 // rowHeight(行高) 等於 90
上例便是如下程式碼的縮寫:
1 let contentHeight = 40 2 let hasHeader = true 3 var rowHeight = contentHeight 4 if hasHeader { 5 rowHeight = rowHeight + 50 6 } else { 7 rowHeight = rowHeight + 20 8 } 9 // rowHeight(行高) 等於 90
使用三元條件運算子的例子說明,可以僅用一行程式碼就將行高設為正確的值。這比(不用三元運算子的)第二個例子簡潔得多,並且行高(rowHeight)無需定義為變數,因為不再需要用 if 語句修改其值。
三元條件運算子提供了二選一的高效寫法。但使用三元條件運算子應小心。如果過度使用,其簡明性可能導致程式碼可讀性差。應避免將多個三元條件運算子組合在同一個語句中。
區間運算子
Swift有兩個區間運演算法,是表示值域的簡便寫法。
閉區間運算子
區間運算子(a...b)定義了 a 到 b 的區間範圍,包括 a 和 b 在內。
閉區間運算子在需要遍歷某區間內所有的值時很有用,如在 for-in 迴圈中使用:
1 for index in 1...5 { 2 println("\(index) times 5 is \(index * 5)") 3 //輸出("\(index)乘以 5 得 (\index * 5)") 4 } 5 // 1 乘以 5 得 5 6 // 2 乘以 5 得 10 7 // 3 乘以 5 得 15 8 // 4 乘以 5 得 20 9 // 5 乘以 5 得 25
for-in 語句的更多介紹,詳見 流程控制 一章(後面章節譯到)。
半閉區間運算子
半閉區間運算子(a..b)定義了從 a 到 b 的區間,但 b 不包括在內。說它是半閉區間,是因為第一個值包含在區間內,但最後一個值在區間外。
半閉區間在處理從 0 開始計數的列表時有用,如遍歷陣列,可從 0 數到列表的長度(但不包括長度值本身):
1 let names = ["Anna", "Alex", "Brian", "Jack"] 2 let count = names.count 3 for i in 0..count { 4 println("Person \(i + 1) is called \(names[i])") 5 } 6 // Person 1 名字是 Anna 7 // Person 2 名字是 Alex 8 // Person 3 名字是 Brian 9 // Person 4 名字是 Jack
注意,陣列包含四個元素,但因為是半閉區間,所以 0..count 只數到 3(陣列中最後一個元素的索引號)。陣列更多資訊詳見 陣列 一章(後面章節譯到)。
邏輯運算子
邏輯運算子是對布林邏輯值 true 或 false 的組合操作,Swift 支援 C 及其衍生語言的三種標準邏輯運算子:
· 邏輯非(!a)
· 邏輯與(a && b)
· 邏輯或(a || b)
邏輯非運算子
邏輯非運算子對布林值取反,即 true 變成 false,false 變成true。
邏輯非運算子是一個字首運算子,緊跟在所操作值的前面,沒有任何空格符。可以理解為"非",如下例:
1 let allowedEntry = false 2 if !allowedEntry { 3 println("ACCESS DENIED") 4 } 5 // prints "ACCESS DENIED" 6 //輸出”ACCESS DENIED“
程式碼中的 if !allowedEntry 可以理解為“如果不允許進入”。隨後的下一行程式碼僅當“不允許進入”成立時才會執行;即 allowedEntry 為 false 時才執行。
如上例,布林值常量及變數的名稱應謹慎選擇命名,方可確保程式碼簡明又具可讀性,同時也應避免使用雙重否定或引起混淆的邏輯語句。
邏輯與運算子
(a && b)構造這樣的邏輯表示式:運算子兩側的值均為 true,整個表示式的求值結果才為 true。
如果有一個值為 false,整個表示式便為 false。事實上,如果第一個值為 false,第二個值將不執行求值運算,因為無論它為何值,整個表示式的值都不可能等於 true。這也被稱為短路求值(short-circuit evaluation)。
下面的例子驗證兩個值,當兩個值都為 true 時才能訪問:
1 let enteredDoorCode = true 2 let passedRetinaScan = false 3 if enteredDoorCode && passedRetinaScan { 4 println("Welcome!") 5 } else { 6 println("ACCESS DENIED") 7 } 8 // prints "ACCESS DENIED" 輸出"ACCESS DENIED"
邏輯或運算子
(a || b)屬於中綴運算子,由兩個連續的豎線構成。它用來構造這樣的表示式:當兩個值中有一個為 true時,整個表示式為 true 。
與前面的邏輯與運算子一樣,邏輯或運算子在檢測它的兩個表示式時,也使用“短路求值”法。只要邏輯或表示式左側為 true,其右側將不執行求值運算,因為這時右側的值對整個表示式的結果不再有影響。
下例中,第一個 Bool 值(hasDoorKey)為 false,但第二個值(knowOverridePassword)為 true。因為有一個值為 true,所以整個表示式的求值結果也為 true,因此允許訪問:
1 let hasDoorKey = false 2 let knowsOverridePassword = true 3 if hasDoorKey || knowsOverridePassword { 4 println("Welcome!") 5 } else { 6 println("ACCESS DENIED") 7 } 8 // prints "Welcome!" 輸出"Welcome!"
組合使用邏輯運算子
可以將多個邏輯運算子組合起來構成一個較長的複合表示式。
1 “if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword { 2 println("Welcome!") 3 } else { 4 println("ACCESS DENIED") 5 } 6 // prints "Welcome!" 輸出"Welcome!"
本例使用多個 && 及 || 運算子構成一條較長的複合表示式。不過,&& 與 || 運算子操作的仍然是兩個值,因此該組合表示式實際上是由三個較短的表示式連立而成。它可以這樣理解:
如果我們輸入了正確的門禁密碼、並且通過了視網膜掃描;或者如果我們有門鑰匙;或者如果我們知道緊急的備用密碼,則允許訪問。
根據 enteredDoorCode、passedRetinaScan、hasDoorKey 三個常量推算,前兩個小表示式的值均為 false。不過我們知道緊急的備用密碼(knowsOverridePassword 為 true),因此整個複合表示式的求值結果仍然為 true。
顯式括號
有時(從語法來看)括號並不是必需的,但加上括號卻很有用,它可以讓複雜表示式的易於閱讀。 在上例中,為組合表示式的第一部分加上括號,可使其意圖更明顯:
1 if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword { 2 println("Welcome!") 3 } else { 4 println("ACCESS DENIED") 5 } 6 // prints "Welcome!" //輸出"Welcome!"
括號將前兩個值與其他值分隔開來,使其作為整體邏輯中的一種可選狀態的意思更加明顯。組合表示式的結果不會改變,但對讀者而言,整體意圖更加清晰。可讀性總是優先於簡潔性;應儘可能在合適的地方使用括號,使你的邏輯更加明晰。
謝謝,Swifter-QQ群:362232993,同好者進~
Fork:https://github.com/Joejo/Swift-lesson-for-chinese