IOS開發語言Swift入門連載---基本運算子
運算子是檢查、改變、合并值的特殊符號或短語。例如,加號+ 將兩個數相加(如let i = 1 + 2 )。複雜些的運算例如邏輯與運算子&& (如if enteredDoorCode && passedRetinaScan ),或讓 i 值加1的便捷自增運算子++i 等。
支援大部分標準 C 語言的運算子,且改進許多特性來減少常規編碼錯誤。如:賦值符(= )不傳回值,以防止把想要判斷相等運算子(== )的地方寫成賦值符導致的錯誤。數值運算子(+ ,- ,* ,/ ,% 等)會檢測並不允許值溢出,以此來避免儲存變數時由於變數大於或小於其類型所能承載的範圍時導致的異常結果。當然允許你使用 Swift 的溢出運算子來實現溢出。詳情參見溢出運算子。
區別於 C 語言,在 Swift 中你可以對浮點數進行取餘運算(% ),Swift 還提供了 C 語言沒有的表達兩數之間的值的區間運算子,(a..b 和a…b ),這方便我們表達一個區間內的數值。
本章節只描述了 Swift 中的基本運算子,進階運算子包含了進階運算子,及如何自訂運算子,及如何進行自訂類型的運算子多載。
術語
運算子有一元、二元和三元運算子。
一元運算子對單一操作對象操作(如-a )。一元運算子分前置符和後置運算子,前置運算子需緊排操作對象之前(如!b ),後置運算子需緊跟操作對象之後(如i++ )。
二元運算子操作兩個操作對象(如2 + 3 ),是中置的,因為它們出現在兩個操作對象之間。
三元運算子操作三個操作對象,和 C 語言一樣,Swift 只有一個三元運算子,就是三元條件運算子(a ? b : c )。
受運算子影響的值叫運算元,在運算式1 + 2 中,加號+ 是二元運算子,它的兩個運算元是值1 和2 。
賦值運算子
賦值運算(a = b ),表示用b 的值來初始化或更新a 的值:
let b = 10var a = 5a = 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 從底層幫你避免了這些錯誤碼。
數值運算
中所有數實值型別都支援了基本的四則運算:
加法(+ )
減法(- )
乘法(* )
除法(/ )
1 + 2 // 等於 35 - 3 // 等於 22 * 3 // 等於 610.0 / 2.5 // 等於 4.0
與 C 語言和 Objective-C 不同的是,Swift 預設不允許在數值運算中出現溢出情況。但你可以使用 Swift 的溢出運算子來達到你有目的的溢出(如a &+ b )。詳情參見溢出運算子。
加法運算子也可用於String 的拼接:
"hello, " + "world" // 等於 "hello, world"
兩個Character 值或一個String 和一個Character 值,相加會產生一個新的String 值:
let dog: Character = "d"let cow: Character = "c"let dogCow = dog + cow// 譯者注: 原來的引號內是很可愛的小狗和小牛, 但win os下不支援表情字元, 所以改成了一般字元// dogCow 現在是 "dc"
詳情參見字元,字串的拼接。
求餘運算
求餘運算(a % b )是計算b 的多少倍剛剛好可以容入a ,返回多出來的那部分(餘數)。
注意:
求餘運算(% )在其他語言也叫模數運算。然而嚴格說來,我們看該運算子對負數的操作結果,“求餘”比“模數”更合適些。
我們來談談取餘是怎麼回事,計算9 % 4 ,你先計算出4 的多少倍會剛好可以容入9 中:
2倍,非常好,那餘數是1(用橙色標出)
在 Swift 中這麼來表達:
9 % 4 // 等於 1
為了得到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 的結果是相同的。
浮點數求餘計算
不同於 C 語言和 Objective-C,Swift 中是可以對浮點數進行求餘的。
8 % 2.5 // 等於 0.5
這個例子中,8 除於2.5 等於3 餘0.5 ,所以結果是一個Double 值0.5 。
自增和自增運算
和 C 語言一樣,Swift 也提供了方便對變數本身加1或減1的自增(++ )和自減(–)的運算子。其操作對象可以是整形和浮點型。
var i = 0++i // 現在 i = 1
每調用一次++i ,i 的值就會加1。實際上,++i 是i = i + 1 的簡寫,而–i 是i = i - 1 的簡寫。
++和– 既是前置又是後置運算。++i ,i++ ,–i 和i– 都是有效寫法。
我們需要注意的是這些運算子修改了i 後有一個傳回值。如果你只想修改i的值,那你就可以忽略這個傳回值。但如果你想使用傳回值,你就需要留意前置和後置操作的傳回值是不同的。
當++ 前置的時候,先自増再返回。
當++ 後置的時候,先返回再自增。
例如:
var a = 0let b = ++a //a和b現在都是 1let c = a++ // a 現在 2, 但 c 是 a 自增前的值 1
上述例子,let b = ++a 先把a 加1了再返回a 的值。所以a 和b 都是新值1 。
而let c = a++ ,是先返回了a 的值,然後a 才加1。所以c得到了a 的舊值1,而a加1後變成2。
除非你需要使用i++ 的特性,不然推薦你使用++i 和 –i,因為先修改後返回這樣的行為更符合我們的邏輯。
一元負號
數值的加號或減號可以使用首碼- (即一元負號)來切換:
let three = 3let minusThree = -three // minusThree 等於 -3let plusThree = -minusThree // plusThree 等於 3, 或 "負負3"
一元負號(- )寫在運算元之前,中間沒有空格。
一元正號
一元正號(+ )不做任何改變地返回運算元的值。
let minusSix = -6let alsoMinusSix = +minusSix // alsoMinusSix 等於 -6
雖然一元+ 做無用功,但當你在使用一元負號來表達負數時,你可以使用一元正號來表達正數,如此你的代碼會具有對稱美。
複合賦值(Compound Assignment Operators)
如同強大的 C 語言,Swift 也提供把其他運算子和賦值運算(= )組合的複合賦值運算子,加賦運算(+= )是其中一個例子:
var a = 1a += 2 // a 現在是 3
運算式a += 2 是a = a + 2 的簡寫,一個加賦運算就把加法和賦值兩件事完成了。
注意:
複合賦值運算沒有傳回值,let b = a += 2 這類代碼是錯誤。這不同於上面提到的自增和自減運算子。
在運算式章節裡有複合運算子的完整列表。
比較運算
所有標準 C 語言中的比較運算都可以在 Swift 中使用。
等於(a == b )
不等於(a != b )
大於(a > b )
小於(a < b )
大於等於(a >= b )
小於等於(a <= b )
注意:
也提供恒等=== 和不恒等!== 這兩個比較符來判斷兩個對象是否引用同一個對象執行個體。更多細節在類與結構。
每個比較運算都返回了一個標識運算式是否成立的布爾值:
1 == 1 // true, 因為 1 等於 12 != 1 // true, 因為 2 不等於 12 > 1 // true, 因為 2 大於 11 < 2 // true, 因為 1 小於21 >= 1 // true, 因為 1 大於等於 12 <= 1 // false, 因為 2 並不小於等於 1
比較運算多用於條件陳述式,如if 條件:
let name = "world"if name == "world" { println("hello, world")} else { println("I'm sorry \(name), but I don't recognize you")}// 輸出 "hello, world", 因為 `name` 就是等於 "world"
關於if 語句,請看控制流程。
三元條件運算
三元條件運算的特殊在於它是有三個運算元的運算子,它的原型是 問題 ? 答案1 : 答案2 。它簡潔地表達根據問題 成立與否作出二選一的操作。如果問題 成立,返回答案1 的結果; 如果不成立,返回答案2 的結果。
使用三元條件運算簡化了以下代碼:
if question: { answer1} else { answer2}
這裡有個計算表格行高的例子。如果有表頭,那行高應比內容高度要高出50像素; 如果沒有表頭,只需高出20像素。
let contentHeight = 40let hasHeader = truelet rowHeight = contentHeight + (hasHeader ? 50 : 20)// rowHeight 現在是 90
這樣寫會比下面的代碼簡潔:
let contentHeight = 40let hasHeader = truevar rowHeight = contentHeightif hasHeader { rowHeight = rowHeight + 50} else { rowHeight = rowHeight + 20}// rowHeight 現在是 90
第一段代碼例子使用了三元條件運算,所以一行代碼就能讓我們得到正確答案。這比第二段代碼簡潔得多,無需將rowHeight 定義成變數,因為它的值無需在if 語句中改變。
三元條件運算提供有效率且便捷的方式來表達二選一的選擇。需要注意的事,過度使用三元條件運算就會由簡潔的代碼變成難懂的代碼。我們應避免在一個組合語句使用多個三元條件運算子。
區間運算子
提供了兩個方便表達一個區間的值的運算子。
閉區間運算子
閉區間運算子(a…b )定義一個包含從a 到b (包括a 和b )的所有值的區間。 ? 閉區間運算子在迭代一個區間的所有值時是非常有用的,如在for-in 迴圈中:
for index in 1...5 { println("\(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.countfor i in 0..count { println("第 \(i + 1) 個人叫 \(names[i])")}// 第 1 個人叫 Anna// 第 2 個人叫 Alex// 第 3 個人叫 Brian// 第 4 個人叫 Jack
數組有4個元素,但0..count 只數到3(最後一個元素的下標),因為它是半閉區間。關於數組,請查閱數組。
邏輯運算
邏輯運算的操作對象是邏輯布爾值。Swift 支援基於 C 語言的三個標準邏輯運算。
邏輯非(!a )
邏輯與(a && b )
邏輯或(a || b )
邏輯非
邏輯非運算(!a )對一個布爾值取反,使得true 變false ,false 變true 。
它是一個前置運算子,需出現在運算元之前,且不加空格。讀作非 a ,然後我們看以下例子:
let allowedEntry = falseif !allowedEntry { println("ACCESS DENIED")}// 輸出 "ACCESS DENIED"
if !allowedEntry語句可以讀作 “如果 非 alowed entry。”,接下一行代碼只有在如果 “非 allow entry” 為true ,即allowEntry 為false 時被執行。
在範例程式碼中,小心地選擇布爾常量或變數有助於代碼的可讀性,並且避免使用雙重邏輯非運算,或混亂的邏輯語句。
邏輯與
邏輯與(a && b )表達了只有a 和b 的值都為true 時,整個運算式的值才會是true 。
只要任意一個值為false ,整個運算式的值就為false 。事實上,如果第一個值為false ,那麼是不去計算第二個值的,因為它已經不可能影響整個運算式的結果了。這被稱做 “短路計算(short-circuit evaluation)”。
以下例子,只有兩個Bool值都為true 值的時候才允許進入:
let enteredDoorCode = truelet passedRetinaScan = falseif enteredDoorCode && passedRetinaScan { println("Welcome!")} else { println("ACCESS DENIED")}// 輸出 "ACCESS DENIED"
邏輯或
邏輯或(a || b )是一個由兩個連續的| 組成的中置運算子。它表示了兩個邏輯運算式的其中一個為true ,整個運算式就為true 。
同邏輯與運算類似,邏輯或也是“短路計算”的,當左端的運算式為true時,將不計算右邊的運算式了,因為它不可能改變整個運算式的值了。
以下範例程式碼中,第一個布爾值(hasDoorKey )為false ,但第二個值(knowsOverridePassword )為true ,所以整個表達是true ,於是允許進入:
let hasDoorKey = falselet knowsOverridePassword = trueif hasDoorKey || knowsOverridePassword { println("Welcome!")} else { println("ACCESS DENIED")}// 輸出 "Welcome!"
組合邏輯
我們可以組合多個邏輯運算來表達一個複合邏輯:
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword { println("Welcome!")} else { println("ACCESS DENIED")}// 輸出 "Welcome!"
這個例子使用了含多個&& 和|| 的複合邏輯。但無論怎樣,&& 和|| 始終只能操作兩個值。所以這實際是三個簡單邏輯連續操作的結果。我們來解讀一下:
如果我們輸入了正確的密碼並通過了視網膜掃描; 或者我們有一把有效鑰匙; 又或者我們知道緊急情況下重設的密碼,我們就能把門開啟進入。
前兩種情況,我們都不滿足,所以前兩個簡單邏輯的結果是false ,但是我們是知道緊急情況下重設的密碼的,所以整個複雜運算式的值還是true 。
使用括弧來明確優先順序
為了一個複雜運算式更容易讀懂,在合適的地方使用括弧來明確優先順序是很有效,雖然它並非必要的。在上個關於門的許可權的例子中,我們給第一個部分加個括弧,使用它看起來邏輯更明確:
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword { println("Welcome!")} else { println("ACCESS DENIED")}// 輸出 "Welcome!"
這括弧使得前兩個值被看成整個邏輯表達中獨立的一個部分。雖然有括弧和沒括弧的輸出結果是一樣的,但對於讀代碼的人來說有括弧的代碼更清晰。可讀性比簡潔性更重要,請在可以讓你代碼變清晰地地方加個括弧吧!