OS11,Swift4從入門到精通-04swift基礎運算子
本節文字內容轉載於
http://www.swift51.com/swift4.0/chapter2/02_Basic_Operators.html
本頁包含內容:
術語
賦值運算子
算術運算子
組合賦值運算子
比較運算子
三目運算子
空合運算子
區間運算子
邏輯運算子
運算子是檢查、改變、合併值的特殊符號或短語。例如,加號(+)將兩個數相加(如 let i = 1 + 2)。更復雜的運算例子包括邏輯與運算子 &&(如 if enteredDoorCode && passedRetinaScan)。
Swift 支援大部分標準 C 語言的運算子,且改進許多特性來減少常規編碼錯誤。如:賦值符(=)不返回值,以防止把想要判斷相等運算子(==)的地方寫成賦值符導致的錯誤。算術運算子(+,-,*,/,%等)會檢測並不允許值溢位,以此來避免儲存變數時由於變數大於或小於其型別所能承載的範圍時導致的異常結果。當然允許你使用 Swift 的溢位運算子來實現溢位。詳情參見溢位運算子。
Swift 還提供了 C 語言沒有的區間運算子,例如 a…<b 或 a…b,這方便我們表達一個區間內的數值。
本章節只描述了 Swift 中的基本運算子,高階運算子這章會包含 Swift 中的高階運算子,及如何自定義運算子,及如何進行自定義型別的運算子過載。
術語
運算子分為一元、二元和三元運算子:
一元運算子對單一操作物件操作(如 -a)。一元運算子分前置運算子和後置運算子,前置運算子需緊跟在操作物件之前(如 !b),後置運算子需緊跟在操作物件之後(如 c!)。
二元運算子操作兩個操作物件(如 2 + 3),是中置的,因為它們出現在兩個操作物件之間。
三元運算子操作三個操作物件,和 C 語言一樣,Swift 只有一個三元運算子,就是三目運算子(a ? b : c)。
受運算子影響的值叫運算元,在表示式 1 + 2 中,加號 + 是二元運算子,它的兩個運算元是值 1 和 2。
賦值運算子
賦值運算子(a = b),表示用 b 的值來初始化或更新 a 的值:
let b = 10
var a = 5
a = b
// a 現在等於 10
如果賦值的右邊是一個多元組,它的元素可以馬上被分解成多個常量或變數:
let (x, y) = (1, 2)
// 現在 x 等於 1,y 等於 2
與 C 語言和 Objective-C 不同,Swift 的賦值操作並不返回任何值。所以以下程式碼是錯誤的:
if x = y {
// 此句錯誤, 因為 x = y 並不返回任何值
}
這個特性使你無法把(==)錯寫成(=),由於 if x = y 是錯誤程式碼,Swift 能幫你避免此類錯誤發生。
算術運算子
Swift 中所有數值型別都支援了基本的四則算術運算子:
加法(+)
減法(-)
乘法(*)
除法(/)
1 + 2 // 等於 3
5 - 3 // 等於 2
2 * 3 // 等於 6
10.0 / 2.5 // 等於 4.0
與 C 語言和 Objective-C 不同的是,Swift 預設情況下不允許在數值運算中出現溢位情況。但是你可以使用 Swift 的溢位運算子來實現溢位運算(如 a &+ b)。詳情參見溢位運算子。
加法運算子也可用於 String 的拼接:
"hello, " + “world” // 等於 “hello, world”
求餘運算子
求餘運算子(a % b)是計算 b 的多少倍剛剛好可以容入a,返回多出來的那部分(餘數)。
注意:
求餘運算子(%)在其他語言也叫取模運算子。但是嚴格說來,我們看該運算子對負數的操作結果,「求餘」比「取模」更合適些。
我們來談談取餘是怎麼回事,計算 9 % 4,你先計算出 4 的多少倍會剛好可以容入 9 中:
Art/remainderInteger_2x.png
你可以在 9 中放入兩個 4,那餘數是 1(用橙色標出)。
在 Swift 中可以表達為:
9 % 4 // 等於 1
為了得到 a % b 的結果,% 計算了以下等式,並輸出餘數作為結果:
a = (b × 倍數) + 餘數
當倍數取最大值的時候,就會剛好可以容入 a 中。
把 9 和 4 代入等式中,我們得 1:
9 = (4 × 2) + 1
同樣的方法,我們來計算 -9 % 4:
-9 % 4 // 等於 -1
把 -9 和 4 代入等式,-2 是取到的最大整數:
-9 = (4 × -2) + -1
餘數是 -1。
在對負數 b 求餘時,b 的符號會被忽略。這意味著 a % b 和 a % -b 的結果是相同的。
一元負號運算子
數值的正負號可以使用字首 -(即一元負號符)來切換:
let three = 3
let minusThree = -three // minusThree 等於 -3
let plusThree = -minusThree // plusThree 等於 3, 或 “負負3”
一元負號符(-)寫在運算元之前,中間沒有空格。
一元正號運算子
一元正號符(+)不做任何改變地返回運算元的值:
let minusSix = -6
let alsoMinusSix = +minusSix // alsoMinusSix 等於 -6
雖然一元正號符什麼都不會改變,但當你在使用一元負號來表達負數時,你可以使用一元正號來表達正數,如此你的程式碼會具有對稱美。
組合賦值運算子
如同 C 語言,Swift 也提供把其他運算子和賦值運算(=)組合的組合賦值運算子,組合加運算(+=)是其中一個例子:
var a = 1
a += 2
// a 現在是 3
表示式 a += 2 是 a = a + 2 的簡寫,一個組合加運算就是把加法運算和賦值運算組合成進一個運算子裡,同時完成兩個運算任務。
注意:
複合賦值運算沒有返回值,let b = a += 2這類程式碼是錯誤。這不同於上面提到的自增和自減運算子。
更多 Swift 標準庫運算子的資訊,請看運算子宣告。
比較運算子(Comparison Operators)
所有標準 C 語言中的比較運算子都可以在 Swift 中使用:
等於(a == b)
不等於(a != b)
大於(a > b)
小於(a < b)
大於等於(a >= b)
小於等於(a <= b)
注意: Swift 也提供恆等(=)和不恆等(!)這兩個比較符來判斷兩個物件是否引用同一個物件例項。更多細節在類與結構。
每個比較運算都返回了一個標識表示式是否成立的布林值:
1 == 1 // true, 因為 1 等於 1
2 != 1 // true, 因為 2 不等於 1
2 > 1 // true, 因為 2 大於 1
1 < 2 // true, 因為 1 小於2
1 >= 1 // true, 因為 1 大於等於 1
2 <= 1 // false, 因為 2 並不小於等於 1
比較運算多用於條件語句,如if條件:
let name = “world”
if name == “world” {
print(“hello, world”)
} else {
print(“I’m sorry (name), but I don’t recognize you”)
}
// 輸出 “hello, world”, 因為 name
就是等於 “world”
關於 if 語句,請看控制流。
如果兩個元組的元素相同,且長度相同的話,元組就可以被比較。比較元組大小會按照從左到右、逐值比較的方式,直到發現有兩個值不等時停止。如果所有的值都相等,那麼這一對元組我們就稱它們是相等的。例如:
(1, “zebra”) < (2, “apple”) // true,因為 1 小於 2
(3, “apple”) < (3, “bird”) // true,因為 3 等於 3,但是 apple 小於 bird
(4, “dog”) == (4, “dog”) // true,因為 4 等於 4,dog 等於 dog
在上面的例子中,你可以看到,在第一行中從左到右的比較行為。因為1小於2,所以(1, “zebra”)小於(2, “apple”),不管元組剩下的值如何。所以"zebra"大於"apple"對結果沒有任何影響,因為元組的比較結果已經被第一個元素決定了。不過,當元組的第一個元素相同時候,第二個元素將會用作比較-第二行和第三行程式碼就發生了這樣的比較。
當元組中的元素都可以被比較時,你也可以使用這些運算子來比較它們的大小。例如,像下面展示的程式碼,你可以比較兩個型別為 (String, Int) 的元組,因為 Int 和 String 型別的值可以比較。相反,Bool 不能被比較,也意味著存有布林型別的元組不能被比較。
(“blue”, -1) < (“purple”, 1) // 正常,比較的結果為 true
(“blue”, false) < (“purple”, true) // 錯誤,因為 < 不能比較布林型別
注意:
Swift 標準庫只能比較七個以內元素的元組比較函式。如果你的元組元素超過七個時,你需要自己實現比較運算子。
三目運算子(Ternary Conditional Operator)
三目運算子的特殊在於它是有三個運算元的運算子,它的形式是 問題 ? 答案 1 : 答案 2。它簡潔地表達根據 問題成立與否作出二選一的操作。如果 問題 成立,返回 答案 1 的結果;反之返回 答案 2 的結果。
三目運算子是以下程式碼的縮寫形式:
if question {
answer1
} else {
answer2
}
這裡有個計算表格行高的例子。如果有表頭,那行高應比內容高度要高出 50 點;如果沒有表頭,只需高出 20 點:
let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
// rowHeight 現在是 90
上面的寫法比下面的程式碼更簡潔:
let contentHeight = 40
let hasHeader = true
var rowHeight = contentHeight
if hasHeader {
rowHeight = rowHeight + 50
} else {
rowHeight = rowHeight + 20
}
// rowHeight 現在是 90
第一段程式碼例子使用了三目運算,所以一行程式碼就能讓我們得到正確答案。這比第二段程式碼簡潔得多,無需將 rowHeight 定義成變數,因為它的值無需在 if 語句中改變。
三目運算提供有效率且便捷的方式來表達二選一的選擇。需要注意的事,過度使用三目運算子會使簡潔的程式碼變的難懂。我們應避免在一個組合語句中使用多個三目運算子。
空合運算子(Nil Coalescing Operator)
空合運算子(a ?? b)將對可選型別 a 進行空判斷,如果 a 包含一個值就進行解封,否則就返回一個預設值 b。表示式 a 必須是 Optional 型別。預設值 b 的型別必須要和 a 儲存值的型別保持一致。
空合運算子是對以下程式碼的簡短表達方法:
a != nil ? a! : b
上述程式碼使用了三目運算子。當可選型別 a 的值不為空時,進行強制解封(a!),訪問 a 中的值;反之返回預設值 b。無疑空合運算子(??)提供了一種更為優雅的方式去封裝條件判斷和解封兩種行為,顯得簡潔以及更具可讀性。
注意: 如果 a 為非空值(non-nil),那麼值 b 將不會被計算。這也就是所謂的短路求值。
下文例子採用空合運算子,實現了在預設顏色名和可選自定義顏色名之間抉擇:
let defaultColorName = “red”
var userDefinedColorName: String? //預設值為 nil
var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 的值為空,所以 colorNameToUse 的值為 “red”
userDefinedColorName 變數被定義為一個可選的 String 型別,預設值為 nil。由於 userDefinedColorName 是一個可選型別,我們可以使用空合運算子去判斷其值。在上一個例子中,通過空合運算子為一個名為 colorNameToUse 的變數賦予一個字串型別初始值。 由於 userDefinedColorName 值為空,因此表示式 userDefinedColorName ?? defaultColorName 返回 defaultColorName 的值,即 red。
另一種情況,分配一個非空值(non-nil)給 userDefinedColorName,再次執行空合運算,運算結果為封包在 userDefaultColorName 中的值,而非預設值。
userDefinedColorName = “green”
colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 非空,因此 colorNameToUse 的值為 “green”
區間運算子(Range Operators)
Swift 提供了幾種方便表達一個區間的值的區間運算子。
閉區間運算子
閉區間運算子(a…b)定義一個包含從 a 到 b(包括 a 和 b)的所有值的區間。a 的值不能超過 b。 閉區間運算子在迭代一個區間的所有值時是非常有用的,如在 for-in 迴圈中:
for index in 1…5 {
print("(index) * 5 = (index * 5)")
}
// 1 * 5 = 5
// 2 * 5 = 10
// 3 * 5 = 15
// 4 * 5 = 20
// 5 * 5 = 25
關於 for-in,請看控制流。
半開區間運算子
半開區間運算子(a…<b)定義一個從 a 到 b 但不包括 b 的區間。 之所以稱為半開區間,是因為該區間包含第一個值而不包括最後的值。
半開區間的實用性在於當你使用一個從 0 開始的列表(如陣列)時,非常方便地從0數到列表的長度。
let names = [“Anna”, “Alex”, “Brian”, “Jack”]
let count = names.count
for i in 0…<count {
print(“第 (i + 1) 個人叫 (names[i])”)
}
// 第 1 個人叫 Anna
// 第 2 個人叫 Alex
// 第 3 個人叫 Brian
// 第 4 個人叫 Jack
陣列有 4 個元素,但 0…<count 只數到3(最後一個元素的下標),因為它是半開區間。關於陣列,請查閱陣列。
單側區間
閉區間操作符有另一個表達形式,可以表達往一側無限延伸的區間 —— 例如,一個包含了陣列從索引 2 到結尾的所有值的區間。在這些情況下,你可以省略掉區間操作符一側的值。這種區間叫做單側區間,因為操作符只有一側有值。例如:
for name in names[2…] {
print(name)
}
// Brian
// Jack
for name in names[…2] {
print(name)
}
// Anna
// Alex
// Brian
半開區間操作符也有單側表達形式,附帶上它的最終值。就像你使用區間去包含一個值,最終值並不會落在區間內。例如:
for name in names[…<2] {
print(name)
}
// Anna
// Alex
單側區間不止可以在下標裡使用,也可以在別的情境下使用。你不能遍歷省略了初始值的單側區間,因為遍歷的開端並不明顯。你可以遍歷一個省略最終值的單側區間;然而,由於這種區間無限延伸的特性,請保證你在迴圈裡有一個結束迴圈的分支。你也可以檢視一個單側區間是否包含某個特定的值,就像下面展示的那樣。
let range = …5
range.contains(7) // false
range.contains(4) // true
range.contains(-1) // true
邏輯運算子(Logical Operators)
邏輯運算子的操作物件是邏輯布林值。Swift 支援基於 C 語言的三個標準邏輯運算。
邏輯非(!a)
邏輯與(a && b)
邏輯或(a || b)
邏輯非運算子
邏輯非運算子(!a)對一個布林值取反,使得 true 變 false,false 變 true。
它是一個前置運算子,需緊跟在運算元之前,且不加空格。讀作 非 a ,例子如下:
let allowedEntry = false
if !allowedEntry {
print(“ACCESS DENIED”)
}
// 輸出 “ACCESS DENIED”
if !allowedEntry 語句可以讀作「如果非 allowedEntry」,接下一行程式碼只有在「非 allowedEntry」為 true,即 allowEntry 為 false 時被執行。
在示例程式碼中,小心地選擇布林常量或變數有助於程式碼的可讀性,並且避免使用雙重邏輯非運算,或混亂的邏輯語句。
邏輯與運算子
邏輯與運算子(a && b)表達了只有 a 和 b 的值都為 true 時,整個表示式的值才會是 true。
只要任意一個值為 false,整個表示式的值就為 false。事實上,如果第一個值為 false,那麼是不去計算第二個值的,因為它已經不可能影響整個表示式的結果了。這被稱做短路計算(short-circuit evaluation)。
以下例子,只有兩個 Bool 值都為 true 的時候才允許進入 if:
let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
print(“Welcome!”)
} else {
print(“ACCESS DENIED”)
}
// 輸出 “ACCESS DENIED”
邏輯或運算子
邏輯或運算子(a || b)是一個由兩個連續的 | 組成的中置運算子。它表示了兩個邏輯表示式的其中一個為 true,整個表示式就為 true。
同邏輯與運算子類似,邏輯或也是「短路計算」的,當左端的表示式為 true 時,將不計算右邊的表示式了,因為它不可能改變整個表示式的值了。
以下示例程式碼中,第一個布林值(hasDoorKey)為 false,但第二個值(knowsOverridePassword)為 true,所以整個表達是 true,於是允許進入:
let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
print(“Welcome!”)
} else {
print(“ACCESS DENIED”)
}
// 輸出 “Welcome!”
邏輯運算子組合計算
我們可以組合多個邏輯運算子來表達一個複合邏輯:
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
print(“Welcome!”)
} else {
print(“ACCESS DENIED”)
}
// 輸出 “Welcome!”
這個例子使用了含多個 && 和 || 的複合邏輯。但無論怎樣,&& 和 || 始終只能操作兩個值。所以這實際是三個簡單邏輯連續操作的結果。我們來解讀一下:
如果我們輸入了正確的密碼並通過了視網膜掃描,或者我們有一把有效的鑰匙,又或者我們知道緊急情況下重置的密碼,我們就能把門開啟進入。
前兩種情況,我們都不滿足,所以前兩個簡單邏輯的結果是 false,但是我們是知道緊急情況下重置的密碼的,所以整個複雜表示式的值還是 true。
注意: Swift 邏輯操作符 && 和 || 是左結合的,這意味著擁有多元邏輯操作符的複合表示式優先計算最左邊的子表示式。
使用括號來明確優先順序
為了一個複雜表示式更容易讀懂,在合適的地方使用括號來明確優先順序是很有效的,雖然它並非必要的。在上個關於門的許可權的例子中,我們給第一個部分加個括號,使它看起來邏輯更明確:
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
print(“Welcome!”)
} else {
print(“ACCESS DENIED”)
}
// 輸出 “Welcome!”
這括號使得前兩個值被看成整個邏輯表達中獨立的一個部分。雖然有括號和沒括號的輸出結果是一樣的,但對於讀程式碼的人來說有括號的程式碼更清晰。可讀性比簡潔性更重要,請在可以讓你程式碼變清晰的地方加個括號吧!