賦值和算術(shù)運(yùn)算符
基本概念
- 一元運(yùn)算符對(duì)一個(gè)目標(biāo)進(jìn)行操作捞奕。一元前綴運(yùn)算符(如 !b)醉旦,一元后綴運(yùn)算符(b!)藏古。
- 二元運(yùn)算符對(duì)兩個(gè)目標(biāo)進(jìn)行操作(比如 a + b )同時(shí)因?yàn)樗鼈兂霈F(xiàn)在兩個(gè)目標(biāo)之間吧碾,所以是中 綴俺孙。
- 三元運(yùn)算符操作三個(gè)目標(biāo)辣卒。Swift 語(yǔ)言也僅有一個(gè)三元運(yùn)算符掷贾,三元條件運(yùn)算符( a ? b : c )。
Swift 運(yùn)算符的改進(jìn)
- Swift 在支持 C 中的大多數(shù)標(biāo)準(zhǔn)運(yùn)算符的同時(shí)也增加了一些排除常見代碼錯(cuò)誤的能力:
- 賦值符號(hào)( = )不會(huì)返回值荣茫,以防它被誤用于等于符號(hào)( == )的意圖上胯盯。
- 算數(shù)符號(hào)( + , - , * , / , % 以及其他)可以檢測(cè)并阻止值溢出,以避免你在操作比儲(chǔ)存類型 允許的范圍更大或者更小的數(shù)字時(shí)得到各種奇奇怪怪的結(jié)果计露。
賦值運(yùn)算符
- 賦值運(yùn)算符將一個(gè)值賦給另外一個(gè)值博脑。
- 如果賦值符號(hào)右側(cè)是擁有多個(gè)值的元組,它的元素將會(huì)一次性地拆分成常量或者變量票罐。
- Swift 的賦值符號(hào)自身不會(huì)返回值叉趣。
-
賦值運(yùn)算符不會(huì)返回值
算術(shù)運(yùn)算符 - 標(biāo)準(zhǔn)運(yùn)算符
- 標(biāo)準(zhǔn)算術(shù)運(yùn)算符 + - * /
- 加法運(yùn)算符同時(shí)也支持 String 的拼接
- Swift 算術(shù)運(yùn)算符默認(rèn)不允許值溢出
算術(shù)運(yùn)算符 - 余數(shù)運(yùn)算符
- 余數(shù)運(yùn)算符( a % b )可以求出多少個(gè) b 的倍數(shù)能夠剛好放進(jìn) a 中并且返回剩下的值 (就是我們所謂的余數(shù))。
- 當(dāng) a 是負(fù)數(shù)時(shí)也使用相同的方法來(lái)進(jìn)行計(jì)算该押。
-
當(dāng) b 為負(fù)數(shù)時(shí)它的正負(fù)號(hào)被忽略掉了疗杉。這意味著 a % b 與 a % -b 能夠獲得相同的答案。
算術(shù)運(yùn)算符 - 一元
- 數(shù)字值的正負(fù)號(hào)可以用前綴 – 來(lái)切換蚕礼,我們稱之為一元減號(hào)運(yùn)算符烟具。
- 一元減號(hào)運(yùn)算符( - )直接在要進(jìn)行操作的值前邊放置,不加任何空格奠蹬。
- 一元加號(hào)運(yùn)算符 ( + )直接返回它操作的值朝聋,不會(huì)對(duì)其進(jìn)行任何的修改。
溢出運(yùn)算符
- 在默認(rèn)情況下囤躁,當(dāng)向一個(gè)整數(shù)賦超過(guò)它容量的值時(shí)冀痕,Swift 會(huì)報(bào)錯(cuò)而不是生成一個(gè)無(wú)效的數(shù),給 我們操作過(guò)大或者過(guò)小的數(shù)的時(shí)候提供了額外的安全性狸演。
- 同時(shí)提供三個(gè)算數(shù)溢出運(yùn)算符來(lái)讓系統(tǒng)支持整數(shù)溢出運(yùn)算:
溢出加法 ( &+ )
溢出減法 ( &- )
溢出乘法 ( &* )
值溢出
-
數(shù)值可以出現(xiàn)向上溢出或向下溢出
溢出也會(huì)發(fā)生在有符號(hào)整型數(shù)值上言蛇。
-
對(duì)于無(wú)符號(hào)與有符號(hào)整型數(shù)值來(lái)說(shuō),當(dāng)出現(xiàn)上溢時(shí)宵距,它們會(huì)從數(shù)值所能容納的最大數(shù)變成最小的 數(shù)腊尚。同樣的,當(dāng)發(fā)生下溢時(shí)满哪,它們會(huì)從所能容納的最小數(shù)變成最大的數(shù)婿斥。
合并空值運(yùn)算符
- 合并空值運(yùn)算符( a ?? b )如果可選項(xiàng) a 有值則展開,如果沒(méi)有值翩瓜,是 nil 受扳,則返 回默認(rèn)值 b 。
- 表達(dá)式 a 必須是一個(gè)可選類型兔跌。表達(dá)式 b 必須與 a 的儲(chǔ)存類型相同勘高。
- 實(shí)際上是三元運(yùn)算符作用到 Optional 上的縮寫(a != nil ? a! : b)。
-
如果 a 的值是非空的,b 的值將不會(huì)被考慮华望,也就是合并空值運(yùn)算符是短路的蕊蝗。
區(qū)間運(yùn)算符
閉區(qū)間運(yùn)算符
-
閉區(qū)間運(yùn)算符( a...b )定義了從 a 到 b 的一組范圍,并且包含 a 和 b 赖舟。a 的值不 能大于 b 蓬戚。
半開區(qū)間運(yùn)算符
- 半開區(qū)間運(yùn)算符( a..<b )定義了從 a 到 b 但不包括 b 的區(qū)間。
-
如同閉區(qū)間運(yùn)算符宾抓,a 的值也不能大于 b 子漩,如果 a 與 b 的值相等,那返回的區(qū)間將 會(huì)是空的石洗。
單側(cè)區(qū)間
- 閉區(qū)間有另外一種形式來(lái)讓區(qū)間朝一個(gè)方向盡可能的遠(yuǎn)幢泼,這種區(qū)間叫做單側(cè)區(qū)間。
- 半開區(qū)間運(yùn)算符同樣可以有單側(cè)形式讲衫,只需要寫它最終的值缕棵。
-
比如說(shuō),一個(gè)包含數(shù)組所有元素的區(qū)間涉兽,從索引 2 到數(shù)組的結(jié)束招驴。在這種情況下,你可 以省略區(qū)間運(yùn)算符一側(cè)的值枷畏。
- 單側(cè)區(qū)間可以在其他上下文中使用别厘,不僅僅是下標(biāo)。
-
不能遍歷省略了第一個(gè)值的單側(cè)區(qū)間矿辽,因?yàn)楸闅v根本不知道該從哪里開始丹允。你可以遍歷 省略了最終值的單側(cè)區(qū)間。
字符串索引區(qū)間
-
字符串范圍也可以使用區(qū)間運(yùn)算符
倒序索引
-
通過(guò) reversed() 方法袋倔,我們可以將一個(gè)正序循環(huán)變成逆序循環(huán)。
Comparable 區(qū)間
-
區(qū)間運(yùn)算符可以作用在 Comparable 類型上折柠,返回閉區(qū)間和半閉區(qū)間宾娜。
位運(yùn)算符
位取反運(yùn)算符
-
位取反運(yùn)算符( ~ )是對(duì)所有位的數(shù)字進(jìn)行取反操作
位與運(yùn)算符
-
位與運(yùn)算符( & )可以對(duì)兩個(gè)數(shù)的比特位進(jìn)行合并。它會(huì)返回一個(gè)新的數(shù)扇售,只有當(dāng)這兩 個(gè)數(shù)都是 1 的時(shí)候才能返回 1 前塔。
位或運(yùn)算符
-
位或運(yùn)算符( | )可以對(duì)兩個(gè)比特位進(jìn)行比較,然后返回一個(gè)新的數(shù)承冰,只要兩個(gè)操作位任 意一個(gè)為 1 時(shí)华弓,那么對(duì)應(yīng)的位數(shù)就為 1 。
位異或運(yùn)算符
-
位異或運(yùn)算符困乒,或者說(shuō)“互斥或”( ^ )可以對(duì)兩個(gè)數(shù)的比特位進(jìn)行比較寂屏。它返回一個(gè) 新的數(shù),當(dāng)兩個(gè)操作數(shù)的對(duì)應(yīng)位不相同時(shí),該數(shù)的對(duì)應(yīng)位就為 1 迁霎。
位左移和右移運(yùn)算符
- 位左移運(yùn)算符( << )和位右移運(yùn)算符( >> )可以把所有位數(shù)的數(shù)字向左或向右移動(dòng)一 個(gè)確定的位數(shù)吱抚。
- 位左移和右移具有給整數(shù)乘以或除以二的效果。將一個(gè)數(shù)左移一位相當(dāng)于把這個(gè)數(shù)翻 倍考廉,將一個(gè)數(shù)右移一位相當(dāng)于把這個(gè)數(shù)減半秘豹。
無(wú)符號(hào)整數(shù)的移位操作
- 已經(jīng)存在的比特位按指定的位數(shù)進(jìn)行左移和右移
- 任何移動(dòng)超出整型存儲(chǔ)邊界的位都會(huì)被丟棄
-
用 0 來(lái)填充向左或向右移動(dòng)后產(chǎn)生的空白位
有符號(hào)整數(shù)的移位操作
- 有符號(hào)整數(shù)使用它的第一位(所謂的符號(hào)位)來(lái)表示這個(gè)整數(shù)是正數(shù)還是負(fù)數(shù)。符號(hào)位為 0 表示為正 數(shù)昌粤,1 表示為負(fù)數(shù)既绕。
- 其余的位數(shù)(所謂的數(shù)值位)存儲(chǔ)了實(shí)際的值。有符號(hào)正整數(shù)和無(wú)符號(hào)數(shù)的存儲(chǔ)方式是一樣的涮坐,都是 從 0 開始算起岸更。
- 但是負(fù)數(shù)的存儲(chǔ)方式略有不同。它存儲(chǔ)的是 2 的 n 次方減去它的絕對(duì)值膊升,這里的 n 為數(shù)值位的位數(shù)怎炊。
補(bǔ)碼表示的優(yōu)點(diǎn)
-
首先,如果想給 -4 加個(gè) -1 廓译,只需要將這兩個(gè)數(shù)的全部八個(gè)比特位相加(包括符號(hào) 位)评肆,并且將計(jì)算結(jié)果中超出的部分丟棄。
-
其次非区,使用二進(jìn)制補(bǔ)碼可以使負(fù)數(shù)的位左移和右移操作得到跟正數(shù)同樣的效果瓜挽,即每向 左移一位就將自身的數(shù)值乘以 2 ,每向右移一位就將自身的數(shù)值除以 2 征绸。要達(dá)到此目 的久橙,對(duì)有符號(hào)整數(shù)的右移有一個(gè)額外的規(guī)則:當(dāng)對(duì)正整數(shù)進(jìn)行位右移操作時(shí),遵循與無(wú) 符號(hào)整數(shù)相同的規(guī)則管怠,但是對(duì)于移位產(chǎn)生的空白位使用符號(hào)位進(jìn)行填充淆衷,而不是 0 。
位運(yùn)算符經(jīng)典算法
兩個(gè)數(shù)字交換
-
不借助臨時(shí)變量渤弛,交換兩個(gè)變量的值祝拯。
求無(wú)符號(hào)整數(shù)二進(jìn)制中 1 的個(gè)數(shù)
給定一個(gè)無(wú)符號(hào)整型(UInt)變量,求其二進(jìn)制表示中 “1” 的個(gè)數(shù)她肯,要求算法的執(zhí)行 效率盡可能的高佳头。
思路:看一個(gè)八位整數(shù) 10 100 001 ,先判斷最后一位是否為 1 晴氨,而“與”操作可以達(dá) 到目的康嘉。可以把這個(gè)八位的數(shù)字與 00000001 進(jìn)行“與”操作籽前。如果結(jié)果為 1 亭珍,則表示 當(dāng)前八位數(shù)的最后一位為 1 敷钾,否則為 0 。怎么判斷第二位呢?向右移位块蚌,再延續(xù)前面的 判斷即可闰非。
- 如果整數(shù)的二進(jìn)制中有較多的 0 ,那么我們每一次右移一位做判斷會(huì)很浪費(fèi)峭范,怎么改進(jìn) 前面的算法呢?有沒(méi)有辦法讓算法的復(fù)雜度只與“1”的個(gè)數(shù)有關(guān)?
思路:為了簡(jiǎn)化這個(gè)問(wèn)題财松,我們考慮只有高位有 1 的情況。例如:11 000 000 纱控,如何跳過(guò)前面低 位的 6 個(gè) 0 辆毡,而直接判斷第七位的 1 ?我們可以設(shè)計(jì) 11 000 000 和 10 111 111(也就是 11 000 000 - 1)做“與”操作,消去最低位的 1 甜害。如果得到的結(jié)果為 0 舶掖,說(shuō)明我們已經(jīng)找到/消去 里面最后一個(gè) 1 。如果不為 0 尔店,那么說(shuō)明我們消去了最低位的 1 眨攘,但是二進(jìn)制中還有其他的 1 , 我們的計(jì)數(shù)器需要加 1 嚣州,然后繼續(xù)上面的操作鲫售。
引申:如何判斷一個(gè)整數(shù)位為 2 的整數(shù)次冪
- 給定一個(gè)無(wú)符號(hào)整型(UInt)變量,判斷是否為 2 的整數(shù)次冪该肴。
-
思路:一個(gè)整數(shù)如果是 2 的整數(shù)次方情竹,那么它的二進(jìn)制表示中有且只有一位是 1 ,而其 它所有位都是 0 匀哄。 根據(jù)前面的分析秦效,把這個(gè)整數(shù)減去 1 后再和它自己做與運(yùn)算,這個(gè)整 數(shù)中唯一的 1 就變成 0 了涎嚼,也就是得到的結(jié)尾為 0 阱州。
位運(yùn)算符經(jīng)典算法 2
缺失的數(shù)字
- 很多成對(duì)出現(xiàn)的正整數(shù)保存在磁盤文件中,注意成對(duì)的數(shù)字不一定是相鄰的铸抑,如 2, 3, 4, 3, 4, 2......贡耽,由于意外有一個(gè)數(shù)字消失了,如何盡快找到是哪個(gè)數(shù)字消失了?
- 思路:考慮“異或”操作的定義鹊汛,當(dāng)兩個(gè)操作數(shù)的對(duì)應(yīng)位不相同時(shí),該數(shù)的對(duì)應(yīng)位就為 1 阱冶。也就是說(shuō)如果是相等的兩個(gè)數(shù)“異或”刁憋,得到的結(jié)果為 0 ,而 0 與任何數(shù)字“異 或”木蹬,得到的是那個(gè)數(shù)字本身至耻。所以我們考慮將所有的數(shù)字做“異或”操作,因?yàn)橹挥?一個(gè)數(shù)字消失,那么其他兩兩出現(xiàn)的數(shù)字“異或”后為 0 尘颓,0 與僅有的一個(gè)的數(shù)字做 “異或”走触,我們就得到了消失的數(shù)字是哪個(gè)。
如果有兩個(gè)數(shù)字意外丟失了(丟失的不是相等的數(shù)字)疤苹,該如何找到丟失的兩個(gè)數(shù)字?
-
思路:設(shè)題目中這兩個(gè)只出現(xiàn) 1 次的數(shù)字分別為 A 和 B互广,如果能將 A,B 分開到二個(gè)數(shù)組 中卧土,那顯然符合“異或”解法的關(guān)鍵點(diǎn)了惫皱。因此這個(gè)題目的關(guān)鍵點(diǎn)就是將 A,B 分開到二個(gè)數(shù) 組中尤莺。由于 A旅敷,B 肯定是不相等的,因此在二進(jìn)制上必定有一位是不同的颤霎。根據(jù)這一位是 0 還 是 1 可以將 A 和 B 分開到 A組 和 B組媳谁。而這個(gè)數(shù)組中其它數(shù)字要么就屬于 A 組,要么就屬 于 B 組友酱。再對(duì) A組 和 B組 分別執(zhí)行“異或”解法就可以得到 A晴音,B 了。而要判斷 A粹污,B 在哪 一位上不相同段多,只要根據(jù) “A 異或 B” 的結(jié)果就可以知道了,這個(gè)結(jié)果在二進(jìn)制上為 1 的位 都說(shuō)明 A壮吩,B 在這一位上是不相同的进苍。
數(shù)組中,只有一個(gè)數(shù)出現(xiàn)一次鸭叙,剩下都出現(xiàn)三次觉啊,找出出現(xiàn)一次的數(shù)字。
運(yùn)算符的優(yōu)先級(jí)和結(jié)合性
- 運(yùn)算符的優(yōu)先級(jí)使得一些運(yùn)算符優(yōu)先于其他運(yùn)算符沈贝,高優(yōu)先級(jí)的運(yùn)算符會(huì)先被計(jì)算杠人。
- 結(jié)合性定義了具有相同優(yōu)先級(jí)的運(yùn)算符是如何結(jié)合(或關(guān)聯(lián))的——是與左邊結(jié)合為一 組,還是與右邊結(jié)合為一組宋下∥松疲可以這樣理解:“它們是與左邊的表達(dá)式結(jié)合的”或者 “它們是與右邊的表達(dá)式結(jié)合的”。
-
Swift 語(yǔ)言中邏輯運(yùn)算符 && 和 || 是左相關(guān)的学歧,這意味著多個(gè)邏輯運(yùn)算符組合的表達(dá)式會(huì) 首先計(jì)算最左邊的子表達(dá)式罩引。
運(yùn)算符方法
運(yùn)算符重載
-
類和結(jié)構(gòu)體可以為現(xiàn)有的運(yùn)算符提供自定義的實(shí)現(xiàn),稱為運(yùn)算符重載枝笨。
一元運(yùn)算符重載
- 類與結(jié)構(gòu)體也能提供標(biāo)準(zhǔn)一元運(yùn)算符的實(shí)現(xiàn)袁铐。
- 要實(shí)現(xiàn)前綴或者后綴運(yùn)算符揭蜒,需要在聲明運(yùn)算符函數(shù)的時(shí)候在 func 關(guān)鍵字之前指定 prefix 或者 postfix 限定符。
組合賦值運(yùn)算符重載
- 組合賦值運(yùn)算符將賦值運(yùn)算符( = )與其它運(yùn)算符進(jìn)行結(jié)合剔桨。
- 在實(shí)現(xiàn)的時(shí)候屉更,需要把運(yùn)算符的左參數(shù)設(shè)置成 inout 類型,因?yàn)檫@個(gè)參數(shù)的值會(huì)在運(yùn)算 符函數(shù)內(nèi)直接被修改洒缀。
等價(jià)運(yùn)算符重載
自定義類和結(jié)構(gòu)體不接收等價(jià)運(yùn)算符的默認(rèn)實(shí)現(xiàn)瑰谜,也就是所謂的“等于”運(yùn)算符( == ) 和“不等于”運(yùn)算符( != )。
-
要使用等價(jià)運(yùn)算符來(lái)檢查你自己類型的等價(jià)帝洪,需要和其他中綴運(yùn)算符一樣提供一個(gè)“等 于”運(yùn)算符似舵,并且遵循標(biāo)準(zhǔn)庫(kù)的 Equatable 協(xié)議
-
Swift 為以下自定義類型提供等價(jià)運(yùn)算符合成實(shí)現(xiàn):
只擁有遵循 Equatable 協(xié)議存儲(chǔ)屬性的結(jié)構(gòu)體
只擁有遵循 Equatable 協(xié)議關(guān)聯(lián)類型的枚舉
沒(méi)有關(guān)聯(lián)類型的枚舉
自定義運(yùn)算符
- 除了實(shí)現(xiàn)標(biāo)準(zhǔn)運(yùn)算符,在 Swift 當(dāng)中還可以聲明和實(shí)現(xiàn)自定義運(yùn)算符(custom operators)葱峡。
- 新的運(yùn)算符要在全局作用域內(nèi)砚哗,使用 operator 關(guān)鍵字進(jìn)行聲明,同時(shí)還要指定 prefix 砰奕、infix 或者 postfix 限定符蛛芥。
自定義中綴運(yùn)算符的優(yōu)先級(jí)和結(jié)合性
- 自定義的中綴( infix )運(yùn)算符也可以指定優(yōu)先級(jí)和結(jié)合性
- 每一個(gè)自定義的中綴運(yùn)算符都屬于一個(gè)優(yōu)先級(jí)組
- 優(yōu)先級(jí)組指定了自定義中綴運(yùn)算符和其他中綴運(yùn)算符的關(guān)系