Swift學(xué)習(xí)有問(wèn)必答群 : 313838956 . 本群由Guards翻譯組創(chuàng)建并維護(hù)
入群須知:
0.1 群主晚上每天20點(diǎn)--21點(diǎn)定時(shí)回答Swift相關(guān)的問(wèn)題.
0.2 群主會(huì)在看到問(wèn)題后, 第一時(shí)間回復(fù)
0.3 拒絕長(zhǎng)時(shí)間潛水, 拒絕討論和Swift , iOS 無(wú)關(guān)的問(wèn)題
該文章翻譯自Apple官方文檔: The Swift 4 Programming Language
Guards翻譯組 正在翻譯Swift 4的全套文檔, 這是該文檔第二章節(jié)《Basic Operators》的下半部分, 上半部分在這里,第一章節(jié)《The Basics》 上半部分請(qǐng)點(diǎn)這里/下半部分請(qǐng)點(diǎn)這里, 原文鏈接: Basic Operators
翻譯 : Jonhory (簡(jiǎn)書(shū) / GitHub)
校對(duì) : Stevin三天三夜 (Blog / GitHub)
譯者心聲
我們會(huì)不定期的更新翻譯文章, Guards翻譯組下周內(nèi)會(huì)發(fā)布 Strings and Characters 章節(jié)中文版. 如感興趣,可以關(guān)注我們的簡(jiǎn)書(shū)
我們是一群熱愛(ài)翻譯并且熱愛(ài) Swift 的人, 希望通過(guò)自己的努力讓不熟悉英語(yǔ)的程序員也能學(xué)習(xí)到國(guó)外的高質(zhì)量的文章. 如發(fā)現(xiàn)文章中翻譯錯(cuò)誤之處, 煩請(qǐng)跟我們聯(lián)系, 我們第一時(shí)間更正.
本篇包含內(nèi)容:
- 空合運(yùn)算符
- 區(qū)間運(yùn)算符
- 邏輯運(yùn)算符
空合運(yùn)算符(Nil-Coalescing Operator)
空合運(yùn)算符(a ?? b
)會(huì)對(duì)可選類(lèi)型a
嘗試解封,如果a
包含一個(gè)值就進(jìn)行解封,如果a
為nil
則返回一個(gè)默認(rèn)值b
。表達(dá)式a
必須是一個(gè)可選(Optional
)類(lèi)型。表達(dá)式b
的類(lèi)型必須跟a
存儲(chǔ)的值的類(lèi)型一致。
空合運(yùn)算符是對(duì)下面的代碼的簡(jiǎn)短表達(dá):
a != nil ? a! : b
上述代碼使用了三目運(yùn)算符。當(dāng)可選類(lèi)型a
的值不為空時(shí),進(jìn)行強(qiáng)制解封(a!
)以訪(fǎng)問(wèn)a
的值,否則返回默認(rèn)值b
。空合運(yùn)算符提供了一種更優(yōu)雅的方式去封裝條件判斷和解封兩種行為。
NOTE:
如果
a
為非空值(non-nil
),那么值b
不會(huì)進(jìn)行計(jì)算。這就是所謂的短路求值。
下面的例子使用空合運(yùn)算符,實(shí)現(xiàn)了在默認(rèn)顏色名和可選自定義顏色名之間的選擇:
let defaultColorName = "red"
var userDefinedColorName: String? // 默認(rèn)值為 nil
var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 值為 nil, 所以 colorNameToUse 值被設(shè)置為 "red"
userDefinedColorName
變量被定義為一個(gè)可選的String
類(lèi)型,默認(rèn)值為nil
。因?yàn)?code>userDefinedColorName是一個(gè)可選類(lèi)型,你可以使用空合運(yùn)算符來(lái)判斷它的值。在上一個(gè)例子中,空合運(yùn)算符被用于初始化一個(gè)名為colorNameToUse
字符串(String
)類(lèi)型變量。因?yàn)?code>userDefinedColorName值為nil
,所以表達(dá)式userDefinedColorName ?? defaultColorName
會(huì)返回defaultColorName
的值,即"red"
。
如果你分配一個(gè)非空值給userDefinedColorName
,再次執(zhí)行空合運(yùn)算,運(yùn)算結(jié)果為userDefinedColorName
解封之后的值而不是默認(rèn)值(defaultColorName
)。
userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 的值非 nil, 所以 colorNameToUse 的值被設(shè)置為 "green"
區(qū)間運(yùn)算符(Range Operators)
Swift 提供幾個(gè)方便表達(dá)一個(gè)區(qū)間的值的區(qū)間運(yùn)算符。
閉區(qū)間運(yùn)算符
閉區(qū)間運(yùn)算符(a...b
)定義一個(gè)包含a
和b
(包括a
和b
)的所有值的區(qū)間。a
的值不能超過(guò)b
的值。
閉區(qū)間運(yùn)算符在遍歷一個(gè)區(qū)間的所有值時(shí)是非常有用的,如在for-in
循環(huán)中:
for index in 1...5 {
print("\(index) times 5 is \(index * 5)")
}
// 1 * 5 = 5
// 2 * 5 = 10
// 3 * 5 = 15
// 4 * 5 = 20
// 5 * 5 = 25
關(guān)于for-in
循環(huán),請(qǐng)查閱Control Flow。
半開(kāi)區(qū)間運(yùn)算符
半開(kāi)區(qū)間運(yùn)算符(a..<b
)定了一個(gè)從a
到b
但不包括b
的區(qū)間。之所以被稱(chēng)為半開(kāi)區(qū)間運(yùn)算符,是因?yàn)樗ǖ谝粋€(gè)值但不包括最后一個(gè)值。如同閉區(qū)間運(yùn)算符,半開(kāi)間運(yùn)算符的a
的值不能超過(guò)b
的值。
如果a
、b
的值相等,運(yùn)算范圍的結(jié)果將為空。
半開(kāi)區(qū)間運(yùn)算符在你使用一個(gè)從0開(kāi)始的列表(如數(shù)組)時(shí)尤其有效,非常方便地從0數(shù)到(但不包括)列表的長(zhǎng)度:
let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..<count {
print("Person \(i + 1) is called \(names[i])")
}
// 第1個(gè)人叫 Anna
// 第2個(gè)人叫 Alex
// 第3個(gè)人叫 Brian
// 第4個(gè)人叫 Jack
注意這個(gè)數(shù)組有4個(gè)元素,但0..<count
只數(shù)到3
(該數(shù)組最后一個(gè)元素的下標(biāo)),因?yàn)樗前腴_(kāi)區(qū)間運(yùn)算符。關(guān)于數(shù)組,請(qǐng)查閱Arrays。
單向區(qū)間
閉區(qū)間運(yùn)算符在某一個(gè)方向上的連續(xù)取值有另一種形式的表達(dá)——比如,一個(gè)包含所有數(shù)組元素的區(qū)間,從下標(biāo)2到數(shù)組的末尾。在這些情況下,你可以省略范圍運(yùn)算符一側(cè)的值。這種區(qū)間被稱(chēng)為單向區(qū)間是因?yàn)樵撨\(yùn)算符只需一側(cè)的值。如下:
for name in names[2...] {
print(name)
}
// Brian
// Jack
for name in names[...2] {
print(name)
}
// Anna
// Alex
// Brian
半開(kāi)區(qū)間運(yùn)算符在有最后的值的時(shí)候也有單向區(qū)間表達(dá)。就像你需要一個(gè)在范圍內(nèi)但不包括末尾的區(qū)間的時(shí)候。如下:
for name in names[..<2] {
print(name)
}
// Anna
// Alex
單向區(qū)間可以在別的上下文中使用,不止是下標(biāo)。你不能遍歷省略第一個(gè)值的單向區(qū)間,因?yàn)椴幻鞔_從哪里開(kāi)始遍歷。你可以遍歷省略最終值的單向區(qū)間;然而,因?yàn)檫@個(gè)區(qū)間會(huì)無(wú)限循環(huán),請(qǐng)確保為該循環(huán)添加明確的結(jié)束條件。你可以檢查一個(gè)單向區(qū)間是否包含特定值,如下面的代碼所示。
let range = ...5
range.contains(7) // false
range.contains(4) // true
range.contains(-1) // true
邏輯運(yùn)算符(Logical Operators)
邏輯運(yùn)算符的操作對(duì)象是邏輯布爾值。Swift 支持基于 C 語(yǔ)言的三個(gè)標(biāo)準(zhǔn)邏輯運(yùn)算符:
- 邏輯非(
!a
) - 邏輯與(
a && b
) - 邏輯或(
a || b
)
邏輯非運(yùn)算符
邏輯非運(yùn)算符(!a
)對(duì)一個(gè)布爾值取反,使true
變false
,使false
變true
。
邏輯非運(yùn)算符是一個(gè)前置運(yùn)算符,需要跟在操作數(shù)之前,且不加空格。讀作“非a”
,例子如下:
let allowedEntry = false
if !allowedEntry {
print("ACCESS DENIED")
}
// 輸出 "ACCESS DENIED"
語(yǔ)句if ! allowedEntry
可以讀作“如果非allowedEntry
”。接下一行代碼只有在“非allowedEntry
”時(shí)為true
,即allowedEntry
為false
時(shí)被執(zhí)行。
在上述例子中,小心地選擇布爾常量或變量有助于代碼的可讀性,同時(shí)應(yīng)避免使用雙重邏輯非或混亂的邏輯語(yǔ)句。
邏輯與運(yùn)算符
邏輯與運(yùn)算符(a && b
)表達(dá)式只有在a
和b
的值都為true
時(shí),整個(gè)表達(dá)式才會(huì)是true
。
只要任一值為false
,整個(gè)表達(dá)式的值為false
。事實(shí)上,如果第一個(gè)值為false
,則第二個(gè)值將不被運(yùn)算,因?yàn)樗豢赡苡绊懻麄€(gè)表達(dá)式的結(jié)果。這被稱(chēng)為*短路計(jì)算(short-circuit evaluation)*
。
如下例,只有兩個(gè)Bool
值都true
時(shí)才允許進(jìn)入if:
let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 輸出 "ACCESS DENIED"
邏輯或運(yùn)算符
邏輯或運(yùn)算符(a || b
)是一個(gè)由兩個(gè)連續(xù)的|
組成的中置運(yùn)算符。它表示了兩個(gè)邏輯表達(dá)式的其中一個(gè)值為true
,整個(gè)表達(dá)式就為true
。
與邏輯與運(yùn)算符類(lèi)似,邏輯或運(yùn)算符也是“短路計(jì)算”的。當(dāng)左側(cè)的表達(dá)式為true
時(shí),將不計(jì)算右側(cè)表達(dá)式,因?yàn)樗粫?huì)改變整個(gè)表達(dá)式的值。
在下面的例子中,第一個(gè)Bool
值(hasDoorKey
)為false
,但第二個(gè)值(knowsOverridePassword
)為true
。因?yàn)槠渲幸粋€(gè)值為true
,所以整個(gè)表達(dá)式為true
,并且允許進(jìn)入:
let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 輸出 "Welcome!"
邏輯運(yùn)算符組合計(jì)算
你可以組合多個(gè)邏輯運(yùn)算符來(lái)表達(dá)一個(gè)復(fù)合邏輯:
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 輸出 "Welcome!"
在這個(gè)例子中使用了多個(gè)&&
和||
運(yùn)算符來(lái)創(chuàng)建一個(gè)長(zhǎng)復(fù)合邏輯表達(dá)式。然而,&&
和||
運(yùn)算符始終只能操作兩個(gè)值,所以這實(shí)際上是三個(gè)簡(jiǎn)單邏輯連續(xù)操作的結(jié)果。我們來(lái)解讀一下:
如果我們輸入正確的密碼(enteredDoorCode
)并通過(guò)了視網(wǎng)膜掃描(passedRetinaScan
),或者有一個(gè)有效的鑰匙(hasDoorKey
),又或者知道緊急情況下重置的密碼(knowsOverridePassword
),我們就能把門(mén)打開(kāi)并進(jìn)入。
基于enteredDoorCode
、passedRetinaScan
和hasDoorKey
的值,前兩個(gè)簡(jiǎn)單邏輯的結(jié)果是false
,但我們知道緊急情況下重置的密碼(knowsOverridePassword
),所以整個(gè)表達(dá)式的值還是true
。
NOTE
Swift邏輯操作符
&&
和||
是左結(jié)合的,這意味著擁有多元邏輯操作符的復(fù)合表達(dá)式優(yōu)先計(jì)算最左邊的子表達(dá)式。
使用括號(hào)以明確優(yōu)先級(jí)
為了使一個(gè)復(fù)合表達(dá)式更易理解,有時(shí)使用括號(hào)來(lái)明確優(yōu)先級(jí)是有效的,即使它并不是必要的。在關(guān)于門(mén)的權(quán)限的例子中,給第一部分加上括號(hào),使它看起來(lái)邏輯更明確:
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 輸出 "Welcome!"
這個(gè)括號(hào)使前兩個(gè)值被看成整個(gè)邏輯表達(dá)式中獨(dú)立的一部分。雖然整個(gè)表達(dá)式的結(jié)果是一樣的,但對(duì)于讀代碼的人而言有括號(hào)的更易讀懂。可讀性比簡(jiǎn)潔性更重要,請(qǐng)?jiān)诳梢宰屇愦a變清晰的地方加個(gè)括號(hào)吧。