# 基本運算符
*運算符*是檢查、改變、合并值的特殊符號或短語。例如,加號(`+`)將兩個數相加(如 `let i = 1 + 2`)。更復雜的運算例子包括邏輯與運算符 `&&`(如 `if enteredDoorCode && passedRetinaScan`)。
Swift 所支持運算符你可能在別的語言比如 C 語言里已經認識了,同時為了減少常見編碼錯誤對它們做了部分改進。如:賦值符(`=`)不再有返回值,這樣就消除了手誤將判等運算符(`==`)寫成賦值符導致代碼錯誤的缺陷。算術運算符(`+`,`-`,`*`,`/`,`%` 等)的結果會被檢測并禁止值溢出,以此來避免保存變量時由于變量大于或小于其類型所能承載的范圍時導致的異常結果。當然允許你使用 Swift 的溢出運算符來實現溢出。詳情參見 [溢出運算符](./27_Advanced_Operators.md#overflow-operators)。
Swift 還提供了 C 語言沒有的區間運算符,例如 `a..<b` 或 `a...b`,這方便我們表達一個區間內的數值。
本章節只描述了 Swift 中的基本運算符,[高級運算符](./27_Advanced_Operators.md) 這章會包含 Swift 中的高級運算符,及如何自定義運算符,及如何進行自定義類型的運算符重載。
## 術語 {#terminology}
運算符分為一元、二元和三元運算符:
- *一元*運算符對單一操作對象操作(如 `-a`)。一元運算符分前置運算符和后置運算符,*前置運算符*需緊跟在操作對象之前(如 `!b`),*后置運算符*需緊跟在操作對象之后(如 `c!`)。
- *二元*運算符操作兩個操作對象(如 `2 + 3`),是*中置*的,因為它們出現在兩個操作對象之間。
- *三元*運算符操作三個操作對象,和 C 語言一樣,Swift 只有一個三元運算符,就是三目運算符(`a ? b : c`)。
受運算符影響的值叫*操作數*,在表達式 `1 + 2` 中,加號 `+` 是二元運算符,它的兩個操作數是值 `1` 和 `2`。
## 賦值運算符 {#assignment-operator}
*賦值運算符*(`a = b`),表示用 `b` 的值來初始化或更新 `a` 的值:
```swift
let b = 10
var a = 5
a = b
// a 現在等于 10
```
如果賦值的右邊是一個多元組,它的元素可以馬上被分解成多個常量或變量:
```swift
let (x, y) = (1, 2)
// 現在 x 等于 1,y 等于 2
```
與 C 語言和 Objective-C 不同,Swift 的賦值操作并不返回任何值。所以下面語句是無效的:
```swift
if x = y {
// 此句錯誤,因為 x = y 并不返回任何值
}
```
通過將 `if x = y` 標記為無效語句,Swift 能幫你避免把 (`==`)錯寫成(`=`)這類錯誤的出現。
## 算術運算符 {#arithmetic-operators}
Swift 中所有數值類型都支持了基本的四則*算術運算符*:
- 加法(`+`)
- 減法(`-`)
- 乘法(`*`)
- 除法(`/`)
```swift
1 + 2 // 等于 3
5 - 3 // 等于 2
2 * 3 // 等于 6
10.0 / 2.5 // 等于 4.0
```
與 C 語言和 Objective-C 不同的是,Swift 默認情況下不允許在數值運算中出現溢出情況。但是你可以使用 Swift 的溢出運算符來實現溢出運算(如 `a &+ b`)。詳情參見 [溢出運算符](./27_Advanced_Operators.md#overflow-operators)。
加法運算符也可用于 `String` 的拼接:
```swift
"hello, " + "world" // 等于 "hello, world"
```
### 求余運算符 {#remainder-operator}
*求余運算符*(`a % b`)是計算 `b` 的多少倍剛剛好可以容入 `a`,返回多出來的那部分(余數)。
> 注意
>
> 求余運算符(`%`)在其他語言也叫*取模運算符*。但是嚴格說來,我們看該運算符對負數的操作結果,「求余」比「取模」更合適些。
我們來談談取余是怎么回事,計算 `9 % 4`,你先計算出 `4` 的多少倍會剛好可以容入 `9` 中:

你可以在 `9` 中放入兩個 `4`,那余數是 1(用橙色標出)。
在 Swift 中可以表達為:
```swift
9 % 4 // 等于 1
```
為了得到 `a % b` 的結果,`%` 計算了以下等式,并輸出 `余數`作為結果:
a = (b × 倍數) + 余數
當 `倍數`取最大值的時候,就會剛好可以容入 `a` 中。
把 `9` 和 `4` 代入等式中,我們得 `1`:
9 = (4 × 2) + 1
同樣的方法,我們來計算 `-9 % 4`:
```swift
-9 % 4 // 等于 -1
```
把 `-9` 和 `4` 代入等式,`-2` 是取到的最大整數:
-9 = (4 × -2) + -1
余數是 `-1`。
在對負數 `b` 求余時,`b` 的符號會被忽略。這意味著 `a % b` 和 `a % -b` 的結果是相同的。
### 一元負號運算符 {#unary-minus-operator}
數值的正負號可以使用前綴 `-`(即*一元負號符*)來切換:
```swift
let three = 3
let minusThree = -three // minusThree 等于 -3
let plusThree = -minusThree // plusThree 等于 3, 或 "負負3"
```
一元負號符(`-`)寫在操作數之前,中間沒有空格。
### 一元正號運算符 {#unary-plus-operator}
*一元正號符*(`+`)不做任何改變地返回操作數的值:
```swift
let minusSix = -6
let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6
```
雖然一元正號符什么都不會改變,但當你在使用一元負號來表達負數時,你可以使用一元正號來表達正數,如此你的代碼會具有對稱美。
## 組合賦值運算符 {#compound-assignment-operators}
如同 C 語言,Swift 也提供把其他運算符和賦值運算(`=`)組合的*組合賦值運算符*,組合加運算(`+=`)是其中一個例子:
```swift
var a = 1
a += 2
// a 現在是 3
```
表達式 `a += 2` 是 `a = a + 2` 的簡寫,一個組合加運算就是把加法運算和賦值運算組合成進一個運算符里,同時完成兩個運算任務。
> 注意
>
> 復合賦值運算沒有返回值,`let b = a += 2` 這類代碼是錯誤。這不同于上面提到的自增和自減運算符。
更多 Swift 標準庫運算符的信息,請看 [運算符聲明](https://developer.apple.com/documentation/swift/operator_declarations)。
?
## 比較運算符(Comparison Operators) {#comparison-operators}
Swift 支持以下的比較運算符:
- 等于(`a == b`)
- 不等于(`a != b`)
- 大于(`a > b`)
- 小于(`a < b`)
- 大于等于(`a >= b`)
- 小于等于(`a <= b`)
> 注意
>
> Swift 也提供恒等(`===`)和不恒等(`!==`)這兩個比較符來判斷兩個對象是否引用同一個對象實例。更多細節在 [類與結構](./09_Structures_And_Classes.md) 章節的 **Identity Operators** 部分。
每個比較運算都返回了一個標識表達式是否成立的布爾值:
```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` 條件:
```swift
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` 語句,請看 [控制流](./05_Control_Flow.md)。
如果兩個元組的元素相同,且長度相同的話,元組就可以被比較。比較元組大小會按照從左到右、逐值比較的方式,直到發現有兩個值不等時停止。如果所有的值都相等,那么這一對元組我們就稱它們是相等的。例如:
```swift
(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` 不能被比較,也意味著存有布爾類型的元組不能被比較。
```swift
("blue", -1) < ("purple", 1) // 正常,比較的結果為 true
("blue", false) < ("purple", true) // 錯誤,因為 < 不能比較布爾類型
```
> 注意
>
> Swift 標準庫只能比較七個以內元素的元組比較函數。如果你的元組元素超過七個時,你需要自己實現比較運算符。
## 三元運算符(Ternary Conditional Operator) {#ternary-conditional-operator}
*三元運算符*的特殊在于它是有三個操作數的運算符,它的形式是 `問題 ? 答案 1 : 答案 2`。它簡潔地表達根據 `問題`成立與否作出二選一的操作。如果 `問題` 成立,返回 `答案 1` 的結果;反之返回 `答案 2` 的結果。
三元運算符是以下代碼的縮寫形式:
```swift
if question {
answer1
} else {
answer2
}
```
這里有個計算表格行高的例子。如果有表頭,那行高應比內容高度要高出 50 點;如果沒有表頭,只需高出 20 點:
```swift
let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
// rowHeight 現在是 90
```
上面的寫法比下面的代碼更簡潔:
```swift
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) {#nil-coalescing-operator}
*空合運算符*(`a ?? b`)將對可選類型 `a` 進行空判斷,如果 `a` 包含一個值就進行解包,否則就返回一個默認值 `b`。表達式 `a` 必須是 Optional 類型。默認值 `b` 的類型必須要和 `a` 存儲值的類型保持一致。
空合運算符是對以下代碼的簡短表達方法:
```swift
a != nil ? a! : b
```
上述代碼使用了三元運算符。當可選類型 `a` 的值不為空時,進行強制解包(`a!`),訪問 `a` 中的值;反之返回默認值 `b`。無疑空合運算符(`??`)提供了一種更為優雅的方式去封裝條件判斷和解包兩種行為,顯得簡潔以及更具可讀性。
> 注意
>
> 如果 `a` 為非空值(`non-nil`),那么值 `b` 將不會被計算。這也就是所謂的*短路求值*。
下文例子采用空合運算符,實現了在默認顏色名和可選自定義顏色名之間抉擇:
```swift
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`,再次執行空合運算,運算結果為封包在 `userDefinedColorName` 中的值,而非默認值。
```swift
userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 非空,因此 colorNameToUse 的值為 "green"
```
## 區間運算符(Range Operators) {#range-operators}
Swift 提供了幾種方便表達一個區間的值的*區間運算符*。
### 閉區間運算符 {#closed-range-operator}
*閉區間運算符*(`a...b`)定義一個包含從 `a` 到 `b`(包括 `a` 和 `b`)的所有值的區間。`a` 的值不能超過 `b`。
閉區間運算符在迭代一個區間的所有值時是非常有用的,如在 `for-in` 循環中:
```swift
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` 循環,請看 [控制流](./05_Control_Flow.md)。
### 半開區間運算符 {#half-open-range-operator}
*半開區間運算符*(`a..<b`)定義一個從 `a` 到 `b` 但不包括 `b` 的區間。
之所以稱為*半開區間*,是因為該區間包含第一個值而不包括最后的值。
半開區間的實用性在于當你使用一個從 0 開始的列表(如數組)時,非常方便地從0數到列表的長度。
```swift
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(最后一個元素的下標),因為它是半開區間。關于數組,請查閱 [數組](./04_Collection_Types.md#arrays)。
### 單側區間 {#one-sided-ranges}
閉區間操作符有另一個表達形式,可以表達往一側無限延伸的區間 —— 例如,一個包含了數組從索引 2 到結尾的所有值的區間。在這些情況下,你可以省略掉區間操作符一側的值。這種區間叫做單側區間,因為操作符只有一側有值。例如:
```swift
for name in names[2...] {
print(name)
}
// Brian
// Jack
for name in names[...2] {
print(name)
}
// Anna
// Alex
// Brian
```
半開區間操作符也有單側表達形式,附帶上它的最終值。就像你使用區間去包含一個值,最終值并不會落在區間內。例如:
```swift
for name in names[..<2] {
print(name)
}
// Anna
// Alex
```
單側區間不止可以在下標里使用,也可以在別的情境下使用。你不能遍歷省略了初始值的單側區間,因為遍歷的開端并不明顯。你可以遍歷一個省略最終值的單側區間;然而,由于這種區間無限延伸的特性,請保證你在循環里有一個結束循環的分支。你也可以查看一個單側區間是否包含某個特定的值,就像下面展示的那樣。
```swift
let range = ...5
range.contains(7) // false
range.contains(4) // true
range.contains(-1) // true
```
## 邏輯運算符(Logical Operators) {#logical-operators}
*邏輯運算符*的操作對象是邏輯布爾值。Swift 支持基于 C 語言的三個標準邏輯運算。
- 邏輯非(`!a`)
- 邏輯與(`a && b`)
- 邏輯或(`a || b`)
### 邏輯非運算符
*邏輯非運算符*(`!a`)對一個布爾值取反,使得 `true` 變 `false`,`false` 變 `true`。
它是一個前置運算符,需緊跟在操作數之前,且不加空格。讀作 `非 a` ,例子如下:
```swift
let allowedEntry = false
if !allowedEntry {
print("ACCESS DENIED")
}
// 輸出“ACCESS DENIED”
```
`if !allowedEntry` 語句可以讀作「如果非 allowedEntry」,接下一行代碼只有在「非 allowedEntry」為 `true`,即 `allowEntry` 為 `false` 時被執行。
在示例代碼中,小心地選擇布爾常量或變量有助于代碼的可讀性,并且避免使用雙重邏輯非運算,或混亂的邏輯語句。
### 邏輯與運算符 {#logical-and-operator}
*邏輯與運算符*(`a && b`)表達了只有 `a` 和 `b` 的值都為 `true` 時,整個表達式的值才會是 `true`。
只要任意一個值為 `false`,整個表達式的值就為 `false`。事實上,如果第一個值為 `false`,那么是不去計算第二個值的,因為它已經不可能影響整個表達式的結果了。這被稱做*短路計算(short-circuit evaluation)*。
以下例子,只有兩個 `Bool` 值都為 `true` 的時候才允許進入 if:
```swift
let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 輸出“ACCESS DENIED”
```
### 邏輯或運算符 {#logical-or-operator}
邏輯或運算符(`a || b`)是一個由兩個連續的 `|` 組成的中置運算符。它表示了兩個邏輯表達式的其中一個為 `true`,整個表達式就為 `true`。
同邏輯與運算符類似,邏輯或也是「短路計算」的,當左端的表達式為 `true` 時,將不計算右邊的表達式了,因為它不可能改變整個表達式的值了。
以下示例代碼中,第一個布爾值(`hasDoorKey`)為 `false`,但第二個值(`knowsOverridePassword`)為 `true`,所以整個表達是 `true`,于是允許進入:
```swift
let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 輸出“Welcome!”
```
### 邏輯運算符組合計算 {#combining-logical-operators}
我們可以組合多個邏輯運算符來表達一個復合邏輯:
```swift
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 輸出“Welcome!”
```
這個例子使用了含多個 `&&` 和 `||` 的復合邏輯。但無論怎樣,`&&` 和 `||` 始終只能操作兩個值。所以這實際是三個簡單邏輯連續操作的結果。我們來解讀一下:
如果我們輸入了正確的密碼并通過了視網膜掃描,或者我們有一把有效的鑰匙,又或者我們知道緊急情況下重置的密碼,我們就能把門打開進入。
前兩種情況,我們都不滿足,所以前兩個簡單邏輯的結果是 `false`,但是我們是知道緊急情況下重置的密碼的,所以整個復雜表達式的值還是 `true`。
> 注意
>
> Swift 邏輯操作符 `&&` 和 `||` 是左結合的,這意味著擁有多元邏輯操作符的復合表達式優先計算最左邊的子表達式。
### 使用括號來明確優先級 {#explicit-parentheses}
為了一個復雜表達式更容易讀懂,在合適的地方使用括號來明確優先級是很有效的,雖然它并非必要的。在上個關于門的權限的例子中,我們給第一個部分加個括號,使它看起來邏輯更明確:
```swift
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 輸出“Welcome!”
```
這括號使得前兩個值被看成整個邏輯表達中獨立的一個部分。雖然有括號和沒括號的輸出結果是一樣的,但對于讀代碼的人來說有括號的代碼更清晰。可讀性比簡潔性更重要,請在可以讓你代碼變清晰的地方加個括號吧!
- 1.關于 Swift
- 2.Swift 初見
- 2-1基礎部分
- 2-2基本運算符
- 2-3字符串和字符
- 2-4集合類型
- 2-5控制流
- 2-6函數
- 2-7閉包
- 2-8枚舉
- 2-9類和結構體
- 2-10屬性
- 2-11方法
- 2-12下標
- 2-13繼承
- 2-14構造過程
- 2-15析構過程
- 2-16可選鏈
- 2-17錯誤處理
- 2-18類型轉換
- 2-19嵌套類型
- 2-20擴展
- 2-21協議
- 2-22泛型
- 2-23不透明類型
- 2-24自動引用計數
- 2-25內存安全
- 2-26訪問控制
- 2-27高級運算符
- 3-1關于語言參考
- 3-2詞法結構
- 3-3類型
- 3-4表達式
- 3-5語句
- 3-6聲明
- 3-7特性
- 3-8模式
- 3-9泛型參數
- 4語法總結