[The Swift Programming Language 中文版]
本頁包含內(nèi)容:
術(shù)語
賦值運(yùn)算符
算術(shù)運(yùn)算符
組合賦值運(yùn)算符
比較運(yùn)算符
三目運(yùn)算符
空合運(yùn)算符
區(qū)間運(yùn)算符
邏輯運(yùn)算符
運(yùn)算符是檢查同规、改變笛辟、合并值的特殊符號或短語鲁森。例如噪馏,加號(+)將兩個數(shù)相加(如 let i = 1 + 2)麦到。更復(fù)雜的運(yùn)算例子包括邏輯與運(yùn)算符 &&(如 if enteredDoorCode && passedRetinaScan)。
Swift 支持大部分標(biāo)準(zhǔn) C 語言的運(yùn)算符欠肾,且改進(jìn)許多特性來減少常規(guī)編碼錯誤瓶颠。如:賦值符(=)不返回值,以防止把想要判斷相等運(yùn)算符(==)的地方寫成賦值符導(dǎo)致的錯誤刺桃。算術(shù)運(yùn)算符(+粹淋,-,*瑟慈,/桃移,%等)會檢測并不允許值溢出,以此來避免保存變量時由于變量大于或小于其類型所能承載的范圍時導(dǎo)致的異常結(jié)果葛碧。當(dāng)然允許你使用 Swift 的溢出運(yùn)算符來實現(xiàn)溢出借杰。詳情參見溢出運(yùn)算符。
區(qū)別于 C 語言进泼,在 Swift 中你可以對浮點(diǎn)數(shù)進(jìn)行取余運(yùn)算(%)蔗衡,Swift 還提供了 C 語言沒有的表達(dá)兩數(shù)之間的值的區(qū)間運(yùn)算符(a..<b 和 a...b)纤虽,這方便我們表達(dá)一個區(qū)間內(nèi)的數(shù)值。
本章節(jié)只描述了 Swift 中的基本運(yùn)算符绞惦,高級運(yùn)算符這章會包含 Swift 中的高級運(yùn)算符逼纸,及如何自定義運(yùn)算符,及如何進(jìn)行自定義類型的運(yùn)算符重載济蝉。
術(shù)語
運(yùn)算符分為一元杰刽、二元和三元運(yùn)算符。
一元運(yùn)算符對單一操作對象操作(如 -a)堆生。一元運(yùn)算符分前置運(yùn)算符和后置運(yùn)算符专缠,前置運(yùn)算符需緊跟在操作對象之前(如 !b),后置運(yùn)算符需緊跟在操作對象之后(如 c!)淑仆。
二元運(yùn)算符操作兩個操作對象(如 2 + 3)涝婉,是中置的,因為它們出現(xiàn)在兩個操作對象之間蔗怠。
三元運(yùn)算符操作三個操作對象墩弯,和 C 語言一樣,Swift 只有一個三元運(yùn)算符寞射,就是三目運(yùn)算符(a ? b : c)渔工。
受運(yùn)算符影響的值叫操作數(shù),在表達(dá)式 1 + 2 中桥温,加號 + 是二元運(yùn)算符引矩,它的兩個操作數(shù)是值 1 和 2。
賦值運(yùn)算符
賦值運(yùn)算(a = b)侵浸,表示用 b 的值來初始化或更新 a 的值:
let b = 10
var a = 5
a = b
// a 現(xiàn)在等于 10
如果賦值的右邊是一個多元組旺韭,它的元素可以馬上被分解成多個常量或變量:
let (x, y) = (1, 2)
// 現(xiàn)在 x 等于 1,y 等于 2
與 C 語言和 Objective-C 不同掏觉,Swift 的賦值操作并不返回任何值区端。所以以下代碼是錯誤的:
if x = y {
// 此句錯誤, 因為 x = y 并不返回任何值
}
這個特性使你無法把(==)錯寫成(=),由于 if x = y 是錯誤代碼澳腹,Swift 能幫你避免此類錯誤發(fā)生织盼。
算術(shù)運(yùn)算符
Swift 中所有數(shù)值類型都支持了基本的四則算術(shù)運(yùn)算:
加法(+)
減法(-)
乘法(*)
除法(/)
1 + 2 // 等于 3
5 - 3 // 等于 2
2 * 3 // 等于 6
10.0 / 2.5 // 等于 4.0
與 C 語言和 Objective-C 不同的是,Swift 默認(rèn)情況下不允許在數(shù)值運(yùn)算中出現(xiàn)溢出情況酱塔。但是你可以使用 Swift 的溢出運(yùn)算符來實現(xiàn)溢出運(yùn)算(如 a &+ b)沥邻。詳情參見溢出運(yùn)算符。
加法運(yùn)算符也可用于 String 的拼接:
"hello, " + "world" // 等于 "hello, world"
求余運(yùn)算符
求余運(yùn)算(a % b)是計算 b 的多少倍剛剛好可以容入a羊娃,返回多出來的那部分(余數(shù))谋国。
注意:
求余運(yùn)算(%)在其他語言也叫取模運(yùn)算。然而嚴(yán)格說來迁沫,我們看該運(yùn)算符對負(fù)數(shù)的操作結(jié)果芦瘾,「求余」比「取模」更合適些集畅。
我們來談?wù)勅∮嗍窃趺椿厥陆埽嬎?9 % 4,你先計算出 4 的多少倍會剛好可以容入 9 中:
你可以在 9 中放入兩個 4挺智,那余數(shù)是 1(用橙色標(biāo)出)祷愉。
在 Swift 中可以表達(dá)為:
9 % 4 // 等于 1
為了得到 a % b 的結(jié)果,% 計算了以下等式赦颇,并輸出余數(shù)作為結(jié)果:
a = (b × 倍數(shù)) + 余數(shù)
當(dāng)倍數(shù)取最大值的時候二鳄,就會剛好可以容入 a 中。
把 9 和 4 代入等式中媒怯,我們得 1:
9 = (4 × 2) + 1
同樣的方法订讼,我們來計算 -9 % 4:
-9 % 4 // 等于 -1
把 -9 和 4 代入等式,-2 是取到的最大整數(shù):
-9 = (4 × -2) + -1
余數(shù)是 -1扇苞。
在對負(fù)數(shù) b 求余時欺殿,b 的符號會被忽略。這意味著 a % b 和 a % -b 的結(jié)果是相同的鳖敷。
浮點(diǎn)數(shù)求余計算
不同于 C 語言和 Objective-C脖苏,Swift 中是可以對浮點(diǎn)數(shù)進(jìn)行求余的。
8 % 2.5 // 等于 0.5
這個例子中定踱,8 除以 2.5 等于 3 余 0.5棍潘,所以結(jié)果是一個 Double 型的值為 0.5。
一元負(fù)號運(yùn)算符
數(shù)值的正負(fù)號可以使用前綴 -(即一元負(fù)號)來切換:
let three = 3
let minusThree = -three // minusThree 等于 -3
let plusThree = -minusThree // plusThree 等于 3, 或 "負(fù)負(fù)3"
一元負(fù)號(-)寫在操作數(shù)之前崖媚,中間沒有空格亦歉。
一元正號運(yùn)算符
一元正號(+)不做任何改變地返回操作數(shù)的值:
let minusSix = -6
let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6
雖然一元 + 什么都不會改變,但當(dāng)你在使用一元負(fù)號來表達(dá)負(fù)數(shù)時至扰,你可以使用一元正號來表達(dá)正數(shù)鳍徽,如此你的代碼會具有對稱美。
組合賦值運(yùn)算符
如同 C 語言敢课,Swift 也提供把其他運(yùn)算符和賦值運(yùn)算(=)組合的組合賦值運(yùn)算符阶祭,組合加運(yùn)算(+=)是其中一個例子:
var a = 1
a += 2
// a 現(xiàn)在是 3
表達(dá)式 a += 2 是 a = a + 2 的簡寫,一個組合加運(yùn)算就是把加法運(yùn)算和賦值運(yùn)算組合成進(jìn)一個運(yùn)算符里直秆,同時完成兩個運(yùn)算任務(wù)濒募。
注意:
復(fù)合賦值運(yùn)算沒有返回值,let b = a += 2這類代碼是錯誤圾结。這不同于上面提到的自增和自減運(yùn)算符瑰剃。
在表達(dá)式章節(jié)里有復(fù)合運(yùn)算符的完整列表。 ?
比較運(yùn)算符(Comparison Operators)
所有標(biāo)準(zhǔn) C 語言中的比較運(yùn)算都可以在 Swift 中使用:
等于(a == b)
不等于(a != b)
大于(a > b)
小于(a < b)
大于等于(a >= b)
小于等于(a <= b)
注意: Swift 也提供恒等(===)和不恒等(!==)這兩個比較符來判斷兩個對象是否引用同一個對象實例筝野。更多細(xì)節(jié)在類與結(jié)構(gòu)晌姚。
每個比較運(yùn)算都返回了一個標(biāo)識表達(dá)式是否成立的布爾值:
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
比較運(yùn)算多用于條件語句粤剧,如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"
關(guān)于 if 語句,請看控制流挥唠。
當(dāng)元組中的值可以比較時抵恋,你也可以使用這些運(yùn)算符來比較它們的大小。例如宝磨,因為 Int 和 String 類型的值可以比較弧关,所以類型為 (Int, String) 的元組也可以被比較。相反唤锉,Bool 不能被比較世囊,也意味著存有布爾類型的元組不能被比較。
比較元組大小會按照從左到右窿祥、逐值比較的方式株憾,直到發(fā)現(xiàn)有兩個值不等時停止。如果所有的值都相等壁肋,那么這一對元組我們就稱它們是相等的号胚。例如:
(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
三目運(yùn)算符(Ternary Conditional Operator)
三目運(yùn)算符的特殊在于它是有三個操作數(shù)的運(yùn)算符弃秆,它的形式是 問題 ? 答案 1 : 答案 2。它簡潔地表達(dá)根據(jù) 問題成立與否作出二選一的操作髓帽。如果 問題 成立菠赚,返回 答案 1 的結(jié)果;反之返回 答案 2 的結(jié)果郑藏。
三目運(yùn)算符是以下代碼的縮寫形式:
if question {
answer1
} else {
answer2
}
這里有個計算表格行高的例子衡查。如果有表頭,那行高應(yīng)比內(nèi)容高度要高出 50 點(diǎn)必盖;如果沒有表頭拌牲,只需高出 20 點(diǎn):
let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
// rowHeight 現(xiàn)在是 90
上面的寫法比下面的代碼更簡潔:
let contentHeight = 40
let hasHeader = true
var rowHeight = contentHeight
if hasHeader {
rowHeight = rowHeight + 50
} else {
rowHeight = rowHeight + 20
}
// rowHeight 現(xiàn)在是 90
第一段代碼例子使用了三目運(yùn)算,所以一行代碼就能讓我們得到正確答案歌粥。這比第二段代碼簡潔得多塌忽,無需將 rowHeight 定義成變量,因為它的值無需在 if 語句中改變失驶。
三目運(yùn)算提供有效率且便捷的方式來表達(dá)二選一的選擇土居。需要注意的事,過度使用三目運(yùn)算符會使簡潔的代碼變的難懂。我們應(yīng)避免在一個組合語句中使用多個三目運(yùn)算符擦耀。
空合運(yùn)算符(Nil Coalescing Operator)
空合運(yùn)算符(a ?? b)將對可選類型 a 進(jìn)行空判斷棉圈,如果 a 包含一個值就進(jìn)行解封,否則就返回一個默認(rèn)值 b埂奈。表達(dá)式 a 必須是 Optional 類型迄损。默認(rèn)值 b 的類型必須要和 a 存儲值的類型保持一致。
空合運(yùn)算符是對以下代碼的簡短表達(dá)方法:
a != nil ? a! : b
上述代碼使用了三目運(yùn)算符账磺。當(dāng)可選類型 a 的值不為空時,進(jìn)行強(qiáng)制解封(a!)痊远,訪問 a 中的值垮抗;反之返回默認(rèn)值 b。無疑空合運(yùn)算符(??)提供了一種更為優(yōu)雅的方式去封裝條件判斷和解封兩種行為碧聪,顯得簡潔以及更具可讀性冒版。
注意: 如果 a 為非空值(non-nil),那么值 b 將不會被計算逞姿。這也就是所謂的短路求值辞嗡。
下文例子采用空合運(yùn)算符,實現(xiàn)了在默認(rèn)顏色名和可選自定義顏色名之間抉擇:
let defaultColorName = "red"
var userDefinedColorName: String? //默認(rèn)值為 nil
var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 的值為空滞造,所以 colorNameToUse 的值為 "red"
userDefinedColorName 變量被定義為一個可選的 String 類型续室,默認(rèn)值為 nil。由于 userDefinedColorName 是一個可選類型谒养,我們可以使用空合運(yùn)算符去判斷其值挺狰。在上一個例子中,通過空合運(yùn)算符為一個名為 colorNameToUse 的變量賦予一個字符串類型初始值买窟。 由于 userDefinedColorName 值為空丰泊,因此表達(dá)式 userDefinedColorName ?? defaultColorName 返回 defaultColorName 的值,即 red始绍。
另一種情況瞳购,分配一個非空值(non-nil)給 userDefinedColorName,再次執(zhí)行空合運(yùn)算亏推,運(yùn)算結(jié)果為封包在 userDefaultColorName 中的值学赛,而非默認(rèn)值。
userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 非空径簿,因此 colorNameToUse 的值為 "green"
區(qū)間運(yùn)算符(Range Operators)
Swift 提供了兩個方便表達(dá)一個區(qū)間的值的運(yùn)算符罢屈。
閉區(qū)間運(yùn)算符
閉區(qū)間運(yùn)算符(a...b)定義一個包含從 a 到 b(包括 a 和 b)的所有值的區(qū)間。a 的值不能超過 b篇亭。 ? 閉區(qū)間運(yùn)算符在迭代一個區(qū)間的所有值時是非常有用的缠捌,如在 for-in 循環(huán)中:
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
關(guān)于 for-in,請看控制流。
半開區(qū)間運(yùn)算符
半開區(qū)間(a..<b)定義一個從 a 到 b 但不包括 b 的區(qū)間曼月。 之所以稱為半開區(qū)間谊却,是因為該區(qū)間包含第一個值而不包括最后的值。
半開區(qū)間的實用性在于當(dāng)你使用一個從 0 開始的列表(如數(shù)組)時哑芹,非常方便地從0數(shù)到列表的長度炎辨。
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
數(shù)組有 4 個元素,但 0..<count 只數(shù)到3(最后一個元素的下標(biāo))聪姿,因為它是半開區(qū)間碴萧。關(guān)于數(shù)組,請查閱數(shù)組末购。
邏輯運(yùn)算(Logical Operators)
邏輯運(yùn)算的操作對象是邏輯布爾值破喻。Swift 支持基于 C 語言的三個標(biāo)準(zhǔn)邏輯運(yùn)算。
邏輯非(!a)
邏輯與(a && b)
邏輯或(a || b)
邏輯非
邏輯非運(yùn)算(!a)對一個布爾值取反盟榴,使得 true 變 false曹质,false 變 true。
它是一個前置運(yùn)算符擎场,需緊跟在操作數(shù)之前羽德,且不加空格。讀作 非 a 迅办,例子如下:
let allowedEntry = false
if !allowedEntry {
print("ACCESS DENIED")
}
// 輸出 "ACCESS DENIED"
if !allowedEntry 語句可以讀作「如果非 allowedEntry」宅静,接下一行代碼只有在「非 allowedEntry」為 true,即 allowEntry 為 false 時被執(zhí)行礼饱。
在示例代碼中坏为,小心地選擇布爾常量或變量有助于代碼的可讀性,并且避免使用雙重邏輯非運(yùn)算镊绪,或混亂的邏輯語句匀伏。
邏輯與
邏輯與(a && b)表達(dá)了只有 a 和 b 的值都為 true 時,整個表達(dá)式的值才會是 true蝴韭。
只要任意一個值為 false够颠,整個表達(dá)式的值就為 false。事實上榄鉴,如果第一個值為 false履磨,那么是不去計算第二個值的,因為它已經(jīng)不可能影響整個表達(dá)式的結(jié)果了庆尘。這被稱做「短路計算(short-circuit evaluation)」剃诅。
以下例子,只有兩個 Bool 值都為 true 的時候才允許進(jìn)入 if:
let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 輸出 "ACCESS DENIED"
邏輯或
邏輯或(a || b)是一個由兩個連續(xù)的 | 組成的中置運(yùn)算符驶忌。它表示了兩個邏輯表達(dá)式的其中一個為 true矛辕,整個表達(dá)式就為 true。
同邏輯與運(yùn)算類似,邏輯或也是「短路計算」的聊品,當(dāng)左端的表達(dá)式為 true 時飞蹂,將不計算右邊的表達(dá)式了,因為它不可能改變整個表達(dá)式的值了翻屈。
以下示例代碼中陈哑,第一個布爾值(hasDoorKey)為 false,但第二個值(knowsOverridePassword)為 true伸眶,所以整個表達(dá)是 true惊窖,于是允許進(jìn)入:
let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 輸出 "Welcome!"
邏輯運(yùn)算符組合計算
我們可以組合多個邏輯運(yùn)算來表達(dá)一個復(fù)合邏輯:
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 輸出 "Welcome!"
這個例子使用了含多個 && 和 || 的復(fù)合邏輯。但無論怎樣赚抡,&& 和 || 始終只能操作兩個值爬坑。所以這實際是三個簡單邏輯連續(xù)操作的結(jié)果。我們來解讀一下:
如果我們輸入了正確的密碼并通過了視網(wǎng)膜掃描涂臣,或者我們有一把有效的鑰匙银萍,又或者我們知道緊急情況下重置的密碼前鹅,我們就能把門打開進(jìn)入映企。
前兩種情況衷敌,我們都不滿足次坡,所以前兩個簡單邏輯的結(jié)果是 false叶堆,但是我們是知道緊急情況下重置的密碼的礼殊,所以整個復(fù)雜表達(dá)式的值還是 true屏镊。
注意: Swift 邏輯操作符 && 和 || 是左結(jié)合的哥攘,這意味著擁有多元邏輯操作符的復(fù)合表達(dá)式優(yōu)先計算最左邊的子表達(dá)式剖煌。
使用括號來明確優(yōu)先級
為了一個復(fù)雜表達(dá)式更容易讀懂,在合適的地方使用括號來明確優(yōu)先級是很有效的逝淹,雖然它并非必要的耕姊。在上個關(guān)于門的權(quán)限的例子中,我們給第一個部分加個括號栅葡,使它看起來邏輯更明確:
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 輸出 "Welcome!"
這括號使得前兩個值被看成整個邏輯表達(dá)中獨(dú)立的一個部分茉兰。雖然有括號和沒括號的輸出結(jié)果是一樣的,但對于讀代碼的人來說有括號的代碼更清晰欣簇」媪常可讀性比簡潔性更重要,請在可以讓你代碼變清晰的地方加個括號吧熊咽!