第3章 萬丈高樓平地起——基本操作符
操作符是任何計算機語言的最核心部分之一。例如,四則運算、求余、邏輯運算對于程序都是非常重要的。在Swift語言中,除了支持常規的操作符外(任何語言都支持的操作符),還新增加了很多操作符,以及對部分常規操作符進行了擴展(如求余支持浮點數)。本章將詳細介紹 Swift 支持的最基本,也是最常用的操作符。更復雜的操作符會在后面的章節詳細討論。
本章要點
□ Swift語言支持哪些操作符
□ 賦值操作符
□ 數組操作符
□ 復合賦值操作符
□ 比較操作符
□ 三元條件操作符
□ 區間操作符
□ 邏輯操作符
3.1 操作符的種類
操作符(Operator,或稱為運算符)是所有語言的必需品,Swift也不例外。通常操作符需要和操作數一同出現。根據操作數的個數,可以分為一元、二元和三元操作符。這里的“元”就是指操作數。
□ 一元操作符:只有一個操作數。例如,符號(?)、自加 (++)、自減(??)都屬于一元操作符。一元操作符又分為前置和后置一元操作符。這里的前置和后置是指操作符相對于操作數的位置。例如,-a中的“-”就是前置操作符,而a++中的“++”就屬于后置操作符。
□ 二元操作符:這類操作符是最常用的,絕大多數操作符都屬于這類操作符。例如,最常用的加(+)、減( ?)、乘(*)、除(/)就屬于二元操作符。二元操作符是中置的,也就是說,操作符會出現中兩個操作數之間,例如a + b。
□ 三元操作符:這類操作符可能并不多見。在Swift語言中只有一個三元操作符,就是三元條件操作符:a?b:c。其中a、b、c是操作數,夾在它們之間(“?”和“:”)的是操作符。
3.2 賦值操作符
賦值操作(a = b)表示用b的值來初始化或更新a的值,例如,下面是一些標準的賦值操作。
var a = 10
let b = a
b = 10
a = b
如果等號(=)右側是一個元組類型的值,那么等號左側也必須是一個元組形式的變量或常量。而且在賦值的過程中,右側元組值中每一個分量值會被分別賦給左側對應的變量或常量。
var (x, y, z) = (1,2,3)
// 現在 x 等于 1, y 等于 2
要注意的是,在C語言和Objective-C中,需要Bool值的地方也可以使用整數值代替,0表示false,非0表示true。所以下面的代碼在C或Objective-C中是正確的。
int x = 20
int y = 100
if(x = y) // 可以滿足條件
{
... ...
}
對于 Swift語言來說,賦值語句是不返回值的,所以下面的代碼在 Swift語言中是錯誤的。
var x = 20
var y = 100
if x = y
{
// 無法編譯通過,因為x = y不會返回任何值
}
Swift的這個特性使你無法將等于操作符(==)錯寫成賦值操作符(=)。盡管像Java這樣的語言可以在一定程度上避免這種錯誤(因為在Java中不能用整數代替Bool類型值),但如果賦值的變量正好是一個布爾類型,那么仍然可能錯將(==)錯寫成(=)。
3.3 數值操作符
Swift 語言支持標準的四則運算操作符,其他數值操作符還包括整數求余、浮點數求余、自增、自減、一元負號、一元正號操作符。本節將介紹這些操作符的基本使用方法。
3.3.1 四則運算操作符
Swift 中所有數值類型都支持基本的四則運算。
□ 加法(+)。
□ 減法(?)。
□ 乘法(*)。
□ 除法(/)。
例如,下面是一些數值進行基本運算的例子。
letx= 1+ 2 // x= 3
var y= 5 -3 // y= 2
var abc= 2*3 // abc =6
var c= 10.0 /2.5 // c= 4.0
與 C 語言、Objective-C、Java等語言不同,Swift的數值默認是不允許溢出的。例如,下面的代碼是無法編譯通過的。
let x:Byte = 1234 // 1234超出了Byte的取值范圍
var y:Byte = 12*33 // Swift編譯器在編譯時會計算12*33的值,很明顯,它們的乘積超出了Byte的范圍
如果在計算數值時使用的變量、常量或方法(函數)返回的值,即使數值超出范圍,也是可以編譯通過的,但執行時會拋出異常,也就是說溢出會變成一個運行時錯誤。例如,下面的代碼在運行時程序會中斷。
let x:Byte = 120
var y:Byte = 20 * x // 執行這條語句會拋出異常
如果需要考慮到溢出的情況,可以使用溢出操作符進行運算,例如,a &* b。即使 a和b的乘積超出了數值類型的取值范圍,也會輸出溢出的結果。由于Swift中的一些高級操作符涉及現在還沒講過的概念,所以這些高級操作符(包括溢出操作符)將放到本書的后面講解。
加法(+)運算符不僅能用于數值的相加,也能用于字符和字符、字符和字符串以及字符串和字符串之間的連接。這3種情況下連接的結果都是字符串。
let c1:Character = "a"
let c2:Character = "b"
var s1:String = c1 + c2 // s1 = "ab"
var s2:String = c1 + s1 // s2 = "aab"
var s3:String = s1 + s2 // s3 = "abaab"
3.3.2 整數求余
整數求余(a%b)就是指計算a最多可以容納多少個b,最后多出來那部分就是余數。例如,9 % 4 = 1,這里1就是余數。也就是說,9最多容納2個4,那么剩余的那個1就是余數。
要注意的是,求余操作符(%)在其他語言中又可以稱為取模運算。不過實際上,%可以對負數進行操作,所以“求余”比“取模”可能更合適些。
如果要為求余操作符給出一個計算公式的話,那么這個公式如下。
a = (b ×倍數) + 余數
這個公式也可以是。
余數 = a - (b ×倍數)
如果對負數使用%,那么計算結果也是負數。例如,?9 % 4 = ?1,如果將這個表達式帶入公式,結果就是。
-9 = ( 4 × -2) + -1
所以余數是?1。
注意
如果%后面的操作數(也就是b)為負數,那么這個負號將被忽略。也就是說 a % b = a % (-b)。另外,%后面的操作數如果加負號,要用圓括號括起來。
3.3.3 浮點數求余
與其他語言不同,Swift是可以對浮點數求余的。例如,8.5 % 2.5 = 1.0。浮點數求余也可以使用整數求余的公式。如果將這個表達式代入公式的結果是。
8.5 = (2.5 × 3) + 1.0
3.3.4 自增和自減
和C語言一樣,Swift也提供了使變量增1和減1的操作符。自增使用兩個加號(++),自減使用兩個減號(??)。不過和C語言不同的是操作數可以是整數,也可以是浮點數。
和C語言一樣,Swift 也提供了方便對變量本身加1或減1的自增(++)和自減(??)的運算符。其操作對象可以是整形和浮點型。
自增和自減還分為前自增、后自增;前自減、后自減。這里的前和后是指自增和自減操作符相對于操作數的位置。前自增和前自減操作符位于操作數前面,例如,++a、??b。后自增和后自減操作符位于操作數后面,例如,a++、b??。它們的區別是當操作符位于操作數前面時,先自加或自減,然后再返回變量值。當操作符位于操作數后面時,先返回變量值,然后再自加或自減。
var i = 1
var v = ++i // v = 2
var m = v++ // m = 2
var x = 2.5
x = ++x // x = 3.5
x = x++ // x = 3.5
盡管單獨增加和減少變量值時使用++a、a++、??a和a??都可以。但推薦使用++a和??a,因為在返回變量值之前先增1或減1更符合邏輯。
3.3.5 一元負號和正號
數值的正負號可以使用前綴?(即一元負號)來切換。
let three= 3
letminusThree= -three // minusThree等于 ?3
letplusThree= -minusThree // plusThree等于 3
要注意的是一元負號(?)需要寫在操作數之前,與操作數之間沒有空格。
一元正號(+)不做任何改變地返回操作數的值。使用規則與一元負號相同。盡管一元正好做的是無用功,但當使用一元負號表示一個負數時,使用一元正號表示正數會使你的代碼更容易閱讀,當然,對于某些審美能力很強的人來說,會看出代碼之美。
3.4 復合賦值操作符
復合賦值(Compound Assignment)就是指將其他操作符和賦值操作符組合在一起使用,例如+=、?=都屬于復合賦值操作。基本上屬于C風格的語言(Java、C#、C++等)都支持這類操作符。
我們也可以將復合賦值操作符看著是兩個操作數進行運算并賦值的簡寫形式,例如,a += b相當于a = a + b。
var a = 1
a += 2 //相當于a = a + 2,所以a等于 3
注意
符合賦值運算沒有返回值,也就是說,let b = a += 2 是錯誤的寫法。
3.5 比較操作符
所有在標準C語言中的比較運算都可以在Swift中使用。
□ 等于(a == b)。
□ 不等于(a != b)。
□ 大于(a > b)。
□ 小于(a < b)。
□ 大于等于(a >= b)。
□ 小于等于(a <= b)。
注意
在 Swift 語言中還支持恒等(3 個等號,===)和不恒等(一個感嘆號和兩個等號,!==)兩個比較操作符。這兩個操作符用來判斷兩個對象是否引用了同一個對象實例。更多關于這兩個操作符的細節,將在后面介紹類和結構體時詳細討論。
每一個比較操作符都返回一個Bool類型的值,所以,比較操作符會經常用于if語句中,關于if語句的細節會在后面的章節中詳細討論。
下面是一些使用比較操作符的例子。
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
let name = "bill"
if name == "world" // 此處條件為false
{
println("yes")
}
else // 此處條件為true
{
println("no")
}
3.6 三元條件操作符
Swift中只有一個三元操作符。操作符的原型如下。
邏輯表達式?為true時的答案:為false時的答案
其實三元條件操作符和前面介紹的復合賦值操作符一樣,也是為了簡化表達式而存在的,所以并不是必須的,但使用該操作符卻能提升程序的開發效率,使程序看起來更美觀。
三元條件操作符實際上是簡化了下面的代碼。
if 邏輯表達式
{
為true時的答案
}
else
{
為false時的答案
}
下面是一個實際的例子,在該例子中分別演示了不使用三元邏輯操作符和使用三元邏輯操作符的情況。
var value:Int
var flag:Bool = true
// 不使用三元邏輯操作符的情況
if flag
{
value = 1
}
else
{
value = 2
}
// 使用三元邏輯操作符的情況
value = (flag ? 1 : 2)
3.7 區間操作符
區間操作符可以非常方便地確定一個區間,通常是一個數值的區間。并且可以枚舉這個區間中的每一個值。盡管區間操作符用起來很方便,但并不是所有的語言都支持區間操作符,對于靜態語言來說,Pascal 是支持區間操作符的。不過幸好 Swift 也支持區間操作符,這將會給我們編寫代碼帶來很多方便。
區間操作符分為閉區間操作符和半開半閉操作符。閉區間操作符使用3個點(...)表示,該操作符包含了區間兩端的值。而半開半閉操作符使用兩個點加一個小于號(..<)表示 ,該操作符左側端點的值會被包含,但不包含右側端點的值。
區間操作符在使用for-in循環枚舉數組或字典中元素時非常有用。
let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
// i會從0循環到count – 1
for i in 0..<count
{
println("第 \(i + 1) 個人叫 \(names[i])")
}
執行上面的代碼,會輸出如下的結果。
第 1 個人叫 Anna
第 2 個人叫 Alex
第 3 個人叫 Brian
第 4 個人叫 Jack
當然,我們還可以使用閉區間對數值進行循環。
for i in 1...5
{
print("index = \(i) ")
}
執行上面的代碼,會輸出如下的內容。
index = 1 index = 2 index = 3 index = 4 index = 5
注意
經測試發現,區間操作符是可以使用浮點數的,例如,1...1.2,不過使用浮點數會使Swift程序員不好處理。讀者自己測試下就知道了,如果使用 for-in語句,會無限循環下去,每次都會輸出很大的數。這可能是因為Swift最開始確定的開發理念是盡可能滿足更多的表達方法,估計是能用浮點數的都可以使用浮點數了(如求余)。但可能還沒考慮全(編譯器未實現完整),所以在對區間操作符使用浮點數時,就會出現無法預料的結果,大家還是別用浮點數了。如果要真用浮點數,Swift最好用步長之類的東西。否則沒法循環了。
3.8 邏輯操作符
邏輯運算的操作對象是邏輯布爾值。Swift支持基于C語言的3個標準邏輯運算。
□ 邏輯非(!a)。
□ 邏輯與(a && b)。
□ 邏輯或(a || b)。
3.8.1 邏輯非
邏輯非運算(!a)對一個布爾值取反,使得true變為false,或使false變為true。
邏輯非是一個前置操作符,需出現在操作數的前面,并且操作符和操作數之間不能加空格。讀作“非 a”或“對a取反”,現在來看下面的例子。
let allowed = false
if !allowed
{
println("allowed")
}
else
{
println("denied");
}
執行這段代碼后,會輸出“allowed”。
3.8.2 邏輯與
邏輯與(a && b)表達了只有a和b的值都為true時,整個表達式的值才會是true。只要任意一個值為false,整個表達式的值就為false。事實上,如果第一個值為false,那么是不去計算第二個值的,因為它已經不可能影響整個表達式的結果了。這被稱做“短路計算(short-circuit evaluation)”。
在下面的例子中,只有兩個Bool值都為true值的時候才會執行if語句中的代碼,否則會執行else中的代碼。
let entered = true
let passed = false
if entered && passed
{
println("Welcome!")
}
else
{
println("ACCESS DENIED")
}
在執行上面的代碼后,會輸出“ACCESS DENIED”
3.8.3 邏輯或
邏輯或(a || b)是一個由兩個連續的“|”組成的中置運算符。它表示了兩個邏輯表達式的其中一個為true,整個表達式就為true。
同邏輯與運算類似,邏輯或也是“短路計算”的,當左端的表達式為true時,將不計算右邊的表達式了,因為它不可能改變整個表達式的值了。
在下面的代碼中,第一個布爾值(hasKey)為 false,但第二個值(knowsPassword)為 true,所以整個表達是true,于是允許進入:
let hasKey = false
let knowsPassword = true
if hasKey || knowsPassword
{
println("Welcome!")
}
else
{
println("ACCESS DENIED")
}
執行這段代碼后,會輸出“Welcome!”。
3.8.4 組合邏輯
我們可以組合多個邏輯運算來表達一個復合邏輯。
if entered && passed || hasKey || knowsPassword
{
println("Welcome!")
}
else
{
println("ACCESS DENIED")
}
執行這段代碼后,會輸出“Welcome!”。
如果只是簡單地將邏輯操作符組合在一起,那么系統會從左到右依次計算,例如,對于這段代碼來說,會先計算“entered && passed”,然后用計算結果與hasKey進行邏輯或,最后再用邏輯或的結果與knowsPassword進行邏輯或。
3.8.5 使用圓括號指定優先級
對于一個復雜的表達式,可能需要指定先運算那一對操作數,這就要使用圓括號來確定優先級。例如,下面的表達式就會先將passed和hasKey進行邏輯或,然后再與entered邏輯與,最后與knowsPassword邏輯或。
if entered&&(passed || hasKey) || knowsPassword
{
println("Welcome!")
}
else
{
println("ACCESS DENIED")
}
執行這段代碼后,會輸出“Welcome!”。
如果邏輯表達式比較復雜,建議使用圓括號指定優先級,這樣不僅不易出錯,而且也增加了程序的可讀性。
3.9 小結
盡管 Swift 語言支持的大多數操作符和其他語言類似,但對于剛入門的程序員來說,最好仔細閱讀本章的內容。因為,如果對這些基本的操作符不了解或不能熟練使用的話,會在以后的學習和工作中遇到很大的困難。當然,對于已有多年工作經驗的讀者來說,只需要看一下Swift特有的操作符(如區間操作符)即可,其他內容可直接跳過。