swift2.2 - 高級(jí)運(yùn)算符

高級(jí)運(yùn)算符

文檔地址

作為 基本運(yùn)算符 的補(bǔ)充,Swift 提供了幾個(gè)高級(jí)運(yùn)算符執(zhí)行對(duì)數(shù)傳值進(jìn)行更加復(fù)雜的操作。這樣寫運(yùn)算包括所有你從 C 或 Objective-C 所熟悉的按位操作和移位運(yùn)算符。

與 C 的算術(shù)運(yùn)算符不同,Swift 中算術(shù)運(yùn)算符默認(rèn)是不會(huì)溢出的。溢出行為都會(huì)作為錯(cuò)誤被捕獲。為了允許溢出行為,可以使用 Swift 中另一套默認(rèn)支持的溢出運(yùn)算符,比如溢出加法運(yùn)算符(&+)。所有這些溢出運(yùn)算符都是以 & 符號(hào)開始的。

當(dāng)你定義了你自己的結(jié)構(gòu)體,類以及枚舉的時(shí)候,那么為這些自定義類型也提供 Swift 標(biāo)準(zhǔn)的運(yùn)算符將會(huì)有用的。Swift 簡(jiǎn)化了這些運(yùn)算符的定制實(shí)現(xiàn),并且精確地確定了你創(chuàng)建的每個(gè)類型的運(yùn)算符所具有的行為。

你不會(huì)被預(yù)定義的運(yùn)算符所限制。在 Swift 中你可以自由地定義你自己的中綴,前綴,后綴和賦值運(yùn)算符,以及相對(duì)應(yīng)的優(yōu)先級(jí)和結(jié)合性。這些運(yùn)算符可以像預(yù)先定義的運(yùn)算符一樣在你的代碼里使用,甚至你可以擴(kuò)展已存在的類型來支持你自己定義的運(yùn)算符。

按位運(yùn)算符

按位運(yùn)算符可以操作數(shù)據(jù)結(jié)構(gòu)中每一個(gè)獨(dú)立的位。它們通常被用在底層開發(fā)中,比如圖形編程和創(chuàng)建設(shè)備驅(qū)動(dòng)。按位運(yùn)算符在處理外部資源的原始數(shù)據(jù)時(shí)也非常有用,比如為自定義的通信協(xié)議的數(shù)據(jù)進(jìn)行編碼和解碼。

Swift 支持 C 里面所有的按位運(yùn)算符,具體如下:

按位取反運(yùn)算符

按位取反運(yùn)算符(~)是對(duì)所有位的數(shù)字進(jìn)行取反操作:

bitwiseNOT_2x
bitwiseNOT_2x

按位取反運(yùn)算符是一個(gè)前綴運(yùn)算符,需要直接放在運(yùn)算符的前面,并且不能有空格:

let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits  // equals 11110000

UInt8 類型的整數(shù)有八位,可以存儲(chǔ) 0255 之間的任意值。這個(gè)例子初始化了一個(gè) UInt8 類型的整數(shù),二進(jìn)制為 00001111,前四位全是 0,后四位都是 1。這和十進(jìn)制的 15 是相等的。

然后使用取反運(yùn)算符創(chuàng)建一個(gè)新的常量名為 invertedBits,它和 initialBits 相等,但是所有位都被取反了。0 變?yōu)榱?11 變?yōu)榱?0。invertedBits 的值是 11110000,和十進(jìn)制的無符號(hào)整數(shù) 240 相等。

按位與運(yùn)算符

按位與運(yùn)算符(&)可以對(duì)兩個(gè)數(shù)的比特位進(jìn)行合并。它會(huì)返回一個(gè)新的數(shù),只有當(dāng)這兩個(gè)數(shù)都是 1 的時(shí)候才能返回 1。

bitwiseAND_2x
bitwiseAND_2x

在下面的例子中,firstSixBitslastSixBits 的中間四個(gè)位都為 1。按位與可以把它們合并為一個(gè)新值 00111100,對(duì)應(yīng)十進(jìn)制的值為 60

let firstSixBits: UInt8 = 0b11111100
let lastSixBits: UInt8  = 0b00111111
let middleFourBits = firstSixBits & lastSixBits  // equals 00111100

按位或運(yùn)算符

按位或運(yùn)算符(|)可以對(duì)兩個(gè)比特位進(jìn)行比較,然后返回一個(gè)新數(shù),只要兩個(gè)操作位任意一個(gè)為 1 時(shí),那么對(duì)應(yīng)的位數(shù)都為 1

bitwiseOR_2x
bitwiseOR_2x

在下面的例子中,someBitsmoreBits在不同的位設(shè)置了 1。按位或運(yùn)算符把它們合并為 11111110,對(duì)應(yīng)的十進(jìn)制是無符號(hào)整數(shù) 154。

let someBits: UInt8 = 0b10110010
let moreBits: UInt8 = 0b01011110
let combinedbits = someBits | moreBits  // equals 11111110

按位異或運(yùn)算符

按位異或運(yùn)算符(^)可以對(duì)兩個(gè)數(shù)的比特位進(jìn)行比較。它返回一個(gè)新的數(shù),當(dāng)兩個(gè)操作數(shù)的對(duì)應(yīng)位不相同時(shí),該數(shù)的對(duì)應(yīng)位就為 1

bitwiseXOR_2x
bitwiseXOR_2x

在下面的例子中,firstBitsotherBits 的值有一位設(shè)置設(shè)置為 1,而對(duì)方設(shè)置為 0。按位異或運(yùn)算符會(huì)將這兩個(gè)位上的值設(shè)置為 1,firstBitsotherBits 其他位都設(shè)置為了 0

let firstBits: UInt8 = 0b00010100
let otherBits: UInt8 = 0b00000101
let outputBits = firstBits ^ otherBits  // equals 00010001

按位左移或右移運(yùn)算符

按位左移(<<)或右移(>>)運(yùn)算符可以把所有位數(shù)的數(shù)字向左或向右移動(dòng)一個(gè)確定的位數(shù),但是需要遵守下面定義的規(guī)則。

按位左移或右移具有乘以 2 或除以 2 的效果。將一個(gè)數(shù)左移一位相當(dāng)于把這個(gè)數(shù)乘以 2,將一個(gè)數(shù)右移一位相當(dāng)于把這個(gè)數(shù)除以 2。

無符號(hào)整數(shù)的移位操作

對(duì)無符號(hào)整數(shù)的移位規(guī)則如下:

  1. 已經(jīng)存在的比特位按指定的位數(shù)進(jìn)行左移和右移。

  2. 任何移動(dòng)超出整型存儲(chǔ)邊界的位都會(huì)被丟棄。

  3. 0 來填充向左或向右移動(dòng)后產(chǎn)生的空白位。

這種方法稱之為 邏輯操作。

下圖展示了 11111111 << 1(即把 11111111 向左移動(dòng) 1 位),11111111 >> 1(即把 11111111 向右移動(dòng) 1 位),藍(lán)色的數(shù)字是被移位的,灰色的數(shù)字被舍棄,橙色的數(shù)字 0 是新插入的:

bitshiftUnsigned_2x
bitshiftUnsigned_2x

下面的代碼展示了 Swift 的移位操作:

let shiftBits: UInt8 = 4   // 00000100 in binary
shiftBits << 1             // 00001000
shiftBits << 2             // 00010000
shiftBits << 5             // 10000000
shiftBits << 6             // 00000000
shiftBits >> 2             // 00000001

可以使用移位操作對(duì)其他的數(shù)據(jù)類型進(jìn)行編碼和解碼:

let pink: UInt32 = 0xCC6699
let redComponent = (pink & 0xFF0000) >> 16    // redComponent is 0xCC, or 204
let greenComponent = (pink & 0x00FF00) >> 8   // greenComponent is 0x66, or 102
let blueComponent = pink & 0x0000FF           // blueComponent is 0x99, or 153

這個(gè)示例使用了一個(gè)命名為 pinkUInt32 型常量來存儲(chǔ)層疊樣式表(CSS)中粉色的顏色值。該 CSS 的十六進(jìn)制顏色值 #CC6699, 在 Swift 中表示為 0xCC6699。然后利用按位與運(yùn)算符(&)和按位右移運(yùn)算符(>>)從這個(gè)顏色值中分解出紅(CC)、綠(66)以及藍(lán)(99)三個(gè)部分。

紅色部分是通過對(duì) 0xCC66990xFF0000 進(jìn)行按位與運(yùn)算后得到的。0xFF0000 中的 0 部分作為掩碼,掩蓋了 OxCC6699 中的第二和第三個(gè)字節(jié),使得數(shù)值中的 6699 被忽略,只留下 0xCC0000。

然后,再將這個(gè)數(shù)按向右移動(dòng) 16 位(>> 16)。十六進(jìn)制中每?jī)蓚€(gè)字符表示 8 個(gè)比特位,所以移動(dòng) 16 位后 0xCC0000 就變?yōu)?0x0000CC。這個(gè)數(shù)和 0xCC 是等同的,也就是十進(jìn)制數(shù)值的 204。

同樣的,綠色部分通過對(duì) 0xCC66990x00FF00 進(jìn)行按位與運(yùn)算得到 0x006600。然后將這個(gè)數(shù)向右移動(dòng) 8 位,得到 0x66,也就是十進(jìn)制數(shù)值的 102。

最后,藍(lán)色部分通過對(duì) 0xCC66990x0000FF 進(jìn)行按位與運(yùn)算得到 0x000099。并且不需要進(jìn)行向右移位,所以結(jié)果為 0x99,也就是十進(jìn)制數(shù)值的 153。

有符號(hào)整型的位移操作

對(duì)比無符號(hào)整型來說,有符整型的移位操作相對(duì)復(fù)雜得多,這種復(fù)雜性源于有符號(hào)整數(shù)的二進(jìn)制表現(xiàn)形式。(為了簡(jiǎn)單起見,以下的示例都是基于 8 位有符號(hào)整數(shù)的,但是其中的原理對(duì)任何位數(shù)的有符號(hào)整數(shù)都是通用的。)

有符號(hào)整型使用第一位(稱作符號(hào)位)表示這個(gè)整數(shù)是正數(shù)還是負(fù)數(shù)。符號(hào)位為 0 表示為正數(shù), 1 表示為負(fù)數(shù)。

其余的位數(shù)(稱為數(shù)值位)存儲(chǔ)了實(shí)際的值。有符號(hào)正整數(shù)和無符號(hào)數(shù)的存儲(chǔ)方式是一樣的,都是從 0 開始算起。這是值為 4Int8 型整數(shù)的二進(jìn)制位表現(xiàn)形式:

bitshiftSignedFour_2x
bitshiftSignedFour_2x

符號(hào)位是 0(意味著是一個(gè)正數(shù)),另外 7 位則代表了十進(jìn)制數(shù)值 4 的二進(jìn)制表示。

但是負(fù)數(shù)的存儲(chǔ)方式略有不同。它存儲(chǔ)的是 2n 次方減去它的真實(shí)值絕對(duì)值,這里的 n 為數(shù)值位的位數(shù)。一個(gè) 8 位的數(shù)有 7 個(gè)數(shù)值位,所以是 27 次方,即 128。

這是值為 -4Int8 型整數(shù)的二進(jìn)制位表現(xiàn)形式:

bitshiftSignedMinusFour_2x
bitshiftSignedMinusFour_2x

這次的符號(hào)位為 1,說明這是一個(gè)負(fù)數(shù),另外 7 個(gè)位則代表了數(shù)值 124(即 128 - 4) 的二進(jìn)制表示。

bitshiftSignedMinusFourValue_2x
bitshiftSignedMinusFourValue_2x

負(fù)數(shù)的表示通常被稱為二進(jìn)制補(bǔ)碼(two's complement)表示法。用這種方法來表示負(fù)數(shù)乍看起來有點(diǎn)奇怪,但它有幾個(gè)優(yōu)點(diǎn)。

首先,如果想對(duì) 1-4 進(jìn)行加法操作,我們只需要將這兩個(gè)數(shù)的全部 8 個(gè)比特位進(jìn)行相加,并且將計(jì)算結(jié)果中超出 8 位的數(shù)值丟棄:

bitshiftSignedAddition_2x
bitshiftSignedAddition_2x

其次,使用二進(jìn)制補(bǔ)碼可以使負(fù)數(shù)的按位左移和右移操作得到跟正數(shù)同樣的效果,即每向左移一位就將自身的數(shù)值乘以 2,每向右一位就將自身的數(shù)值除以 2。要達(dá)到此目的,對(duì)有符號(hào)整數(shù)的右移有一個(gè)額外的規(guī)則:

  • 當(dāng)對(duì)正整數(shù)進(jìn)行按位右移操作時(shí),遵循與無符號(hào)整數(shù)相同的規(guī)則,但是對(duì)于移位產(chǎn)生的空白位使用符號(hào)位進(jìn)行填充,而不是用 0。
bitshiftSigned_2x
bitshiftSigned_2x

這個(gè)行為可以確保有符號(hào)整數(shù)的符號(hào)位不會(huì)因?yàn)橛乙撇僮鞫淖?,這通常被稱為算術(shù)移位(arithmetic shift)。

由于正數(shù)和負(fù)數(shù)的特殊存儲(chǔ)方式,在對(duì)它們進(jìn)行右移的時(shí)候,會(huì)使它們?cè)絹碓浇咏?0。在移位的過程中保持符號(hào)位不變,意味著負(fù)整數(shù)在接近 0 的過程中會(huì)一直保持為負(fù)。

溢出運(yùn)算符

在默認(rèn)情況下,當(dāng)向一個(gè)整數(shù)賦超過它容量的值時(shí),Swift 默認(rèn)會(huì)報(bào)錯(cuò),而不是生成一個(gè)無效的數(shù)。這個(gè)行為給我們操作過大或著過小的數(shù)的時(shí)候提供了額外的安全性。

例如,Int16 型整數(shù)能容納的有符號(hào)整數(shù)范圍是 -3276832767,當(dāng)為一個(gè) Int16 型變量賦的值超過這個(gè)范圍時(shí),系統(tǒng)就會(huì)報(bào)錯(cuò):

var potentialOverflow = Int16.max
// potentialOverflow 的值是 32767, 這是 Int16 能容納的最大整數(shù)
potentialOverflow += 1
// 這里會(huì)報(bào)錯(cuò)

為過大或者過小的數(shù)值提供錯(cuò)誤處理,能讓我們?cè)谔幚磉吔缰禃r(shí)更加靈活。

然而,也可以選擇讓系統(tǒng)在數(shù)值溢出的時(shí)候采取截?cái)嗖僮?,而非?bào)錯(cuò)??梢允褂?Swift 提供的三個(gè)溢出操作符(overflow operators)來讓系統(tǒng)支持整數(shù)溢出運(yùn)算。這些操作符都是以 & 開頭的:

  • 溢出加法 &+
  • 溢出減法 &-
  • 溢出乘法 &*

數(shù)值溢出

數(shù)值可能出現(xiàn)向上溢出或向下溢出。

這個(gè)示例演示了當(dāng)對(duì)一個(gè)無符號(hào)整數(shù)使用溢出加法(&+)進(jìn)行上溢運(yùn)算時(shí)會(huì)發(fā)生什么:

var unsignedOverflow = UInt8.max
// unsignedOverflow 等于 UInt8 所能容納的最大整數(shù) 255
unsignedOverflow = unsignedOverflow &+ 1
// 此時(shí) unsignedOverflow 等于 0

unsignedOverflow 被初始化為 UInt8 所能容納的最大整數(shù)(255,二進(jìn)制為 11111111)。溢出加法運(yùn)算符(&+)對(duì)其進(jìn)行加 1 操作。這使得它的二進(jìn)制表示正好超出 UInt8 所能容納的位數(shù),也就導(dǎo)致了數(shù)值的溢出,如下圖所示。數(shù)值溢出后,留在 UInt8 邊界內(nèi)的值是 00000000,也就是十進(jìn)制數(shù)值的 0。

overflowAddition_2x
overflowAddition_2x

同樣地,當(dāng)我們對(duì)一個(gè)無符號(hào)整數(shù)使用溢出減法(&-)進(jìn)行下溢運(yùn)算時(shí)也會(huì)產(chǎn)生類似的現(xiàn)象:

var unsignedOverflow = UInt8.min
// unsignedOverflow 等于 UInt8 所能容納的最小整數(shù) 0
unsignedOverflow = unsignedOverflow &- 1
// 此時(shí) unsignedOverflow 等于 255

UInt8 型整數(shù)能容納的最小值是 0,以二進(jìn)制表示即 00000000。當(dāng)使用溢出減法運(yùn)算符(&-)對(duì)其進(jìn)行減 1 操作時(shí),數(shù)值會(huì)產(chǎn)生下溢并被截?cái)酁?11111111, 也就是十進(jìn)制數(shù)值的 255。

overflowUnsignedSubtraction_2x
overflowUnsignedSubtraction_2x

溢出也會(huì)發(fā)生在有符號(hào)整型數(shù)值上。在對(duì)有符號(hào)整型數(shù)值進(jìn)行溢出加法或溢出減法運(yùn)算時(shí),符號(hào)位也需要參與計(jì)算,正如按位左移/右移運(yùn)算符所描述的。

var signedOverflow = Int8.min
// signedOverflow 等于 Int8 所能容納的最小整數(shù) -128
signedOverflow = signedOverflow &- 1
// 此時(shí) signedOverflow 等于 127

Int8 型整數(shù)能容納的最小值是 -128,以二進(jìn)制表示即 10000000。當(dāng)使用溢出減法操作符對(duì)其進(jìn)行減 1 操作時(shí),符號(hào)位被翻轉(zhuǎn),得到二進(jìn)制數(shù)值 01111111,也就是十進(jìn)制數(shù)值的 127,這個(gè)值也是 Int8 型整數(shù)所能容納的最大值。

overflowSignedSubtraction_2x
overflowSignedSubtraction_2x

對(duì)于無符號(hào)與有符號(hào)整型數(shù)值來說,當(dāng)出現(xiàn)上溢時(shí),它們會(huì)從數(shù)值所能容納的最大數(shù)變成最小的數(shù)。同樣地,當(dāng)發(fā)生下溢時(shí),它們會(huì)從所能容納的最小數(shù)變成最大的數(shù)。

優(yōu)先級(jí)和結(jié)合性

運(yùn)算符的優(yōu)先級(jí)(precedence) 使得一些運(yùn)算符優(yōu)先于其他運(yùn)算符,高優(yōu)先級(jí)的運(yùn)算符會(huì)先被計(jì)算。

結(jié)合性(associativity)定義了具有相同優(yōu)先級(jí)的運(yùn)算符是如何結(jié)合(或關(guān)聯(lián))的 —— 是與左邊結(jié)合為一組,還是與右邊結(jié)合為一組。可以這樣理解:『它們是與左邊的表達(dá)式結(jié)合的』或者『它們是與右邊的表達(dá)式結(jié)合的』。

在復(fù)合表達(dá)式的運(yùn)算順序中,運(yùn)算符的優(yōu)先級(jí)和結(jié)合性是非常重要的。舉例來說,為什么下面這個(gè)表達(dá)式的運(yùn)算結(jié)果是 17?

2 + 3 % 4 * 5
// = 17

如果嚴(yán)格地從左到右進(jìn)行運(yùn)算,則運(yùn)算的過程是這樣的:

  • 2 + 3 = 5
  • 5 % 4 = 1
  • 1 * 5 = 5

然而正確的答案是 17,而不是5。優(yōu)先級(jí)高的運(yùn)算符要先于優(yōu)先級(jí)低的運(yùn)算符進(jìn)行計(jì)算。與 C 語言類似,在 Swift 中,取余運(yùn)算符(%)和乘法運(yùn)算符(*)的優(yōu)先級(jí)高于加法運(yùn)算符(+)。因此,它們的計(jì)算順序要先于加法運(yùn)算。

但是,取余和乘法具有相同的優(yōu)先級(jí)。這時(shí)為了得到正確的運(yùn)算順序,還需要考慮結(jié)合性,乘法與取余運(yùn)算都是左結(jié)合的。可以將這考慮成為這兩部分表達(dá)式都隱式地加上了括號(hào):

2 + ((3 % 4) * 5)

(3 % 4) 是 3,所以表達(dá)式等價(jià)于:

2 + (3 * 5)

(3 * 5) 是 15,所以表達(dá)式等價(jià)于:

2 + 15

此時(shí)可以容易地看出計(jì)算的結(jié)果為 17

如果想查看完整的 Swift 運(yùn)算符優(yōu)先級(jí)和結(jié)合性規(guī)則,請(qǐng)參考表達(dá)式。以及 Swift 標(biāo)準(zhǔn)庫中的運(yùn)算符。

注意:
對(duì)于C語言和 Objective-C 來說,Swift 的運(yùn)算符優(yōu)先級(jí)和結(jié)合性規(guī)則是更加簡(jiǎn)潔和可預(yù)測(cè)的。但是,這也意味著它們于那些基于C的語言不是完全一致的。在對(duì)現(xiàn)有的代碼進(jìn)行移植的時(shí)候,要注意確保運(yùn)算符的行為仍然是按照你所想的那樣去執(zhí)行。

運(yùn)算符函數(shù)

類和結(jié)構(gòu)體可以為現(xiàn)有的操作符提供自定義的實(shí)現(xiàn),這通常被稱為運(yùn)算符重載(overloading)。

下面的例子展示了如何為自定義的結(jié)構(gòu)實(shí)現(xiàn)加法操作符(+)。算術(shù)加法運(yùn)算符是一個(gè)兩目運(yùn)算符(binary operator),因?yàn)樗梢詫?duì)兩個(gè)目標(biāo)進(jìn)行操作,同時(shí)它還是中綴(infix)運(yùn)算符,因?yàn)樗霈F(xiàn)在兩個(gè)目標(biāo)中間。

例子中定義了一個(gè)名為 Vector2D 的結(jié)構(gòu)體用來表示二維坐標(biāo)向量 (x, y),緊接著定義了一個(gè)可以對(duì)兩個(gè) Vector2D 結(jié)構(gòu)體進(jìn)行相加的運(yùn)算符函數(shù)(operator function):

struct Vector2D {
    var x = 0.0, y = 0.0
}
func + (left: Vector2D, right: Vector2D) -> Vector2D {
    return Vector2D(x: left.x + right.x, y: left.y + right.y)
}

該運(yùn)算符函數(shù)被定義為一個(gè)全局函數(shù),并且函數(shù)的名字與它要進(jìn)行重載的 + 名字一致。因?yàn)樗阈g(shù)加法運(yùn)算符是雙目運(yùn)算符,所以這個(gè)運(yùn)算符函數(shù)接收兩個(gè)類型為 Vector2D 的輸入?yún)?shù),同時(shí)有一個(gè) Vector2D 類型的返回值。

在這個(gè)實(shí)現(xiàn)中,輸入?yún)?shù)分別被命名為 leftright,代表在 + 運(yùn)算符左邊和右邊的兩個(gè) Vector2D 對(duì)象。函數(shù)返回了一個(gè)新的 Vector2D 的對(duì)象,這個(gè)對(duì)象的 xy 分別等于兩個(gè)參數(shù)對(duì)象的 xy 的值之和。

這個(gè)函數(shù)被定義成全局的,而不是 Vector2D 結(jié)構(gòu)的成員方法,所以任意兩個(gè) Vector2D 對(duì)象都可以使用這個(gè)中綴運(yùn)算符:

let vector = Vector2D(x: 3.0, y: 1.0)
let anotherVector = Vector2D(x: 2.0, y: 4.0)
let combinedVector = vector + anotherVector
// combinedVector 是一個(gè)新的Vector2D, 值為 (5.0, 5.0)

這個(gè)例子實(shí)現(xiàn)兩個(gè)向量 (3.0,1.0)(2.0,4.0) 的相加,并得到新的向量 (5.0,5.0)。這個(gè)過程如下圖示:

vectorAddition_2x
vectorAddition_2x

前綴和后綴運(yùn)算符

上個(gè)例子演示了一個(gè)雙目中綴運(yùn)算符的自定義實(shí)現(xiàn)。類與結(jié)構(gòu)體也能提供標(biāo)準(zhǔn)單目運(yùn)算符(unary operators)的實(shí)現(xiàn)。單目運(yùn)算符只有一個(gè)操作目標(biāo)。當(dāng)運(yùn)算符出現(xiàn)在操作目標(biāo)之前時(shí),它就是前綴(prefix)的(比如 -a),而當(dāng)它出現(xiàn)在操作目標(biāo)之后時(shí),它就是后綴(postfix)的(比如 i++)。

要實(shí)現(xiàn)前綴或者后綴運(yùn)算符,需要在聲明運(yùn)算符函數(shù)的時(shí)候在 func 關(guān)鍵字之前指定 prefix 或者 postfix 限定符:

prefix func - (vector: Vector2D) -> Vector2D {
    return Vector2D(x: -vector.x, y: -vector.y)
}

這段代碼為 Vector2D 類型實(shí)現(xiàn)了單目減運(yùn)算符(-a)。由于單目減運(yùn)算符是前綴運(yùn)算符,所以這個(gè)函數(shù)需要加上 prefix 限定符。

對(duì)于簡(jiǎn)單數(shù)值,單目減運(yùn)算符可以對(duì)它們的正負(fù)性進(jìn)行改變。對(duì)于 Vector2D 來說,單目減運(yùn)算將其 xy 屬性的正負(fù)性都進(jìn)行了改變。

let positive = Vector2D(x: 3.0, y: 4.0)
let negative = -positive
// negative 是一個(gè)值為 (-3.0, -4.0) 的 Vector2D 實(shí)例
let alsoPositive = -negative
// alsoPositive 是一個(gè)值為 (3.0, 4.0) 的 Vector2D 實(shí)例

復(fù)合賦值運(yùn)算符

復(fù)合賦值運(yùn)算符(Compound assignment operators)將賦值運(yùn)算符(=)與其它運(yùn)算符進(jìn)行結(jié)合。比如,將加法與賦值結(jié)合成加法賦值運(yùn)算符(+=)。在實(shí)現(xiàn)的時(shí)候,需要把運(yùn)算符的左參數(shù)設(shè)置成 inout 類型,因?yàn)檫@個(gè)參數(shù)的值會(huì)在運(yùn)算符函數(shù)內(nèi)直接被修改。

func += (inout left: Vector2D, right: Vector2D) {
    left = left + right
}

因?yàn)榧臃ㄟ\(yùn)算在之前已經(jīng)定義過了,所以在這里無需重新定義。在這里可以直接利用現(xiàn)有的加法運(yùn)算符函數(shù),用它來對(duì)左值和右值進(jìn)行相加,并再次賦值給左值:

var original = Vector2D(x: 1.0, y: 2.0)
let vectorToAdd = Vector2D(x: 3.0, y: 4.0)
original += vectorToAdd
// original 的值現(xiàn)在為 (4.0, 6.0)

注意
不能對(duì)默認(rèn)的賦值運(yùn)算符(=)進(jìn)行重載。只有組合賦值符可以被重載。同樣地,也無法對(duì)三目條件運(yùn)算符a ? b : c 進(jìn)行重載.

等價(jià)操作符

Custom classes and structures do not receive a default implementation of the equivalence operators, known as the “equal to” operator (==) and “not equal to” operator (!=). It is not possible for Swift to guess what would qualify as “equal” for your own custom types, because the meaning of “equal” depends on the roles that those types play in your code.

To use the equivalence operators to check for equivalence of your own custom type, provide an implementation of the operators in the same way as for other infix operators:

自定義的類和結(jié)構(gòu)體沒有對(duì)等價(jià)操作符(equivalence operators)進(jìn)行默認(rèn)實(shí)現(xiàn),等價(jià)操作符通常被稱為『相等』操作符(==)與『不等』操作符(!=)。對(duì)于自定義類型,Swift 無法判斷其是否『相等』,因?yàn)椤合嗟取坏暮x取決于這些自定義類型在你的代碼中所扮演的角色。

為了使用等價(jià)操作符來對(duì)自定義的類型進(jìn)行判等操作,需要為其提供自定義實(shí)現(xiàn),實(shí)現(xiàn)的方法與其它中綴運(yùn)算符一樣:

func == (left: Vector2D, right: Vector2D) -> Bool {
    return (left.x == right.x) && (left.y == right.y)
}
func != (left: Vector2D, right: Vector2D) -> Bool {
    return !(left == right)
}

上述代碼實(shí)現(xiàn)了『相等』運(yùn)算符(==)來判斷兩個(gè) Vector2D 對(duì)象是否有相等。對(duì)于 Vector2D 類型來說,『相等』意味『兩個(gè)實(shí)例的 x屬性 和 y 屬性都相等』,這也是代碼中用來進(jìn)行判等的邏輯。示例里同時(shí)也實(shí)現(xiàn)了『不等』操作符(!=),它簡(jiǎn)單地將『相等』操作符進(jìn)行取反后返回。

現(xiàn)在我們可以使用這兩個(gè)運(yùn)算符來判斷兩個(gè) Vector2D 對(duì)象是否相等。

let twoThree = Vector2D(x: 2.0, y: 3.0)
let anotherTwoThree = Vector2D(x: 2.0, y: 3.0)
if twoThree == anotherTwoThree {
    print("These two vectors are equivalent.")
}
// 輸出 "These two vectors are equivalent."

自定義運(yùn)算符

除了實(shí)現(xiàn)標(biāo)準(zhǔn)運(yùn)算符,在 Swift 當(dāng)中還可以聲明和實(shí)現(xiàn)自定義運(yùn)算符(custom operators)??梢杂脕碜远x運(yùn)算符的字符列表請(qǐng)參考操作符

新的運(yùn)算符要在全局作用域內(nèi),使用 operator 關(guān)鍵字進(jìn)行聲明,同時(shí)還要指定 prefixinfix 或者 postfix 限定符:

prefix operator +++ {}

上面的代碼定義了一個(gè)新的名為 +++ 的前綴運(yùn)算符。對(duì)于這個(gè)運(yùn)算符,在 Swift 中并沒有意義,因?yàn)槲覀冡槍?duì) Vector2D 的實(shí)例來定義它的意義。對(duì)這個(gè)示例來講,+++ 被實(shí)現(xiàn)為『前綴雙自增』運(yùn)算符。它使用了前面定義的復(fù)合加法操作符來讓矩陣對(duì)自身進(jìn)行相加,從而讓 Vector2D 實(shí)例的 x 屬性和 y 屬性的值翻倍:

prefix func +++ (inout vector: Vector2D) -> Vector2D {
    vector += vector
    return vector
}

Vector2D+++ 的實(shí)現(xiàn)和 ++ 的實(shí)現(xiàn)很相似, 唯一不同的是前者對(duì)自身進(jìn)行相加, 而后者是與另一個(gè)值為 (1.0, 1.0) 的向量相加.

var toBeDoubled = Vector2D(x: 1.0, y: 4.0)
let afterDoubling = +++toBeDoubled
// toBeDoubled 現(xiàn)在的值為 (2.0, 8.0)
// afterDoubling 現(xiàn)在的值也為 (2.0, 8.0)

自定義中綴運(yùn)算符的優(yōu)先級(jí)和結(jié)合性

自定義的中綴(infix)運(yùn)算符也可以指定優(yōu)先級(jí)(precedence)和結(jié)合性(associativity)。優(yōu)先級(jí)和結(jié)合性中詳細(xì)闡述了這兩個(gè)特性是如何對(duì)中綴運(yùn)算符的運(yùn)算產(chǎn)生影響的。

結(jié)合性(associativity)可取的值有 leftrightnone。當(dāng)左結(jié)合運(yùn)算符跟其他相同優(yōu)先級(jí)的左結(jié)合運(yùn)算符寫在一起時(shí),會(huì)跟左邊的操作數(shù)進(jìn)行結(jié)合。同理,當(dāng)右結(jié)合運(yùn)算符跟其他相同優(yōu)先級(jí)的右結(jié)合運(yùn)算符寫在一起時(shí),會(huì)跟右邊的操作數(shù)進(jìn)行結(jié)合。而非結(jié)合運(yùn)算符不能跟其他相同優(yōu)先級(jí)的運(yùn)算符寫在一起。

結(jié)合性(associativity)的默認(rèn)值是 none,優(yōu)先級(jí)(precedence)如果沒有指定,則默認(rèn)為 100。

以下例子定義了一個(gè)新的中綴運(yùn)算符 +-,此操作符是左結(jié)合的,并且它的優(yōu)先級(jí)為 140

infix operator +- { associativity left precedence 140 }
func +- (left: Vector2D, right: Vector2D) -> Vector2D {
    return Vector2D(x: left.x + right.x, y: left.y - right.y)
}
let firstVector = Vector2D(x: 1.0, y: 2.0)
let secondVector = Vector2D(x: 3.0, y: 4.0)
let plusMinusVector = firstVector +- secondVector
// plusMinusVector 是一個(gè) Vector2D 類型,并且它的值為 (4.0, -2.0)

這個(gè)運(yùn)算符把兩個(gè)向量的 x 值相加,同時(shí)用第一個(gè)向量的 y 值減去第二個(gè)向量的 y 值。因?yàn)樗举|(zhì)上是屬于『加型』運(yùn)算符,所以將它的結(jié)合性和優(yōu)先級(jí)被設(shè)置(left140),這與 +- 等默認(rèn)的中綴加型操作符是相同的。完整的 Swift 操作符默認(rèn)結(jié)合性與優(yōu)先級(jí)請(qǐng)參考表達(dá)式。

注意:
當(dāng)定義前綴與后綴操作符的時(shí)候,我們并沒有指定優(yōu)先級(jí)。然而,如果對(duì)同一個(gè)操作數(shù)同時(shí)使用前綴與后綴操作符,則后綴操作符會(huì)先被執(zhí)行。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容