swift 操作符重載和自定義操作符

與 Objective-C 不同,Swift 支持重載操作符這樣的特性,最常見(jiàn)的使用方式可能就是定義一些簡(jiǎn)便的計(jì)算了。比如我們需要一個(gè)表示二維向量的數(shù)據(jù)結(jié)構(gòu):

struct Vector2D {
    var x = 0.0
    var y = 0.0
}

一個(gè)很簡(jiǎn)單的需求是兩個(gè) Vector2D 相加:

let v1 = Vector2D(x: 2.0, y: 3.0)
let v2 = Vector2D(x: 1.0, y: 4.0)
let v3 = Vector2D(x: v1.x + v2.x, y: v1.y + v2.y)

如果只做一次的話似乎還好,但是一般情況我們會(huì)進(jìn)行很多這種操作。這樣的話,我們可能更愿意定義一個(gè) Vector2D 相加的操作,來(lái)讓代碼簡(jiǎn)化清晰。

對(duì)于兩個(gè)向量相加,我們可以重載加號(hào)操作符:

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

這樣,上面的 v3 以及之后的所有表示兩個(gè)向量相加的操作就全部可以用加號(hào)來(lái)表達(dá)了:

let v3 = v1 + v2

類似地,我們還可以為 Vector2D 定義像 - (減號(hào),兩個(gè)向量相減) ,- (負(fù)號(hào),單個(gè)向量 x 和 y 同時(shí)取負(fù)) 等等這樣的運(yùn)算符。這個(gè)就作為練習(xí)交給大家。

上面定義的加號(hào),減號(hào)和負(fù)號(hào)都是已經(jīng)存在于 Swift 中的運(yùn)算符了,我們所做的只是變換它的參數(shù)進(jìn)行重載。如果我們想要定義一個(gè)全新的運(yùn)算符的話,要做的事情會(huì)多一件。比如點(diǎn)積運(yùn)算就是一個(gè)在矢量運(yùn)算中很常用的運(yùn)算符,它表示兩個(gè)向量對(duì)應(yīng)坐標(biāo)的乘積的和。根據(jù)定義,以及參考重載運(yùn)算符的方法,我們選取 +* 來(lái)表示這個(gè)運(yùn)算的話,不難寫出:

func +*(left: Vector2D, right: Vector2D) -> Double {
    return letf.x * right.x + left.y * right.y
}

但是編譯器會(huì)給我們一個(gè)錯(cuò)誤:
“Operator implementation without matching operator declaration”

這是因?yàn)槲覀儧](méi)有對(duì)這個(gè)操作符進(jìn)行聲明。之前可以直接重載像 +,-,* 這樣的操作符,是因?yàn)?Swift 中已經(jīng)有定義了,如果我們要新加操作符的話,需要先對(duì)其進(jìn)行聲明,告訴編譯器這個(gè)符號(hào)其實(shí)是一個(gè)操作符。添加如下代碼:

precedencegroup DotProductPrecedence {
    associativity: none
    higherThan: MultiplicationPrecedence
}

infix operator +*: DotProductPrecedence

precedencegroup

定義了一個(gè)操作符優(yōu)先級(jí)別。操作符優(yōu)先級(jí)的定義和類型聲明有些相似,一個(gè)操作符比需要屬于某個(gè)特定的優(yōu)先級(jí)。Swift 標(biāo)準(zhǔn)庫(kù)中已經(jīng)定義了一些常用的運(yùn)算優(yōu)先級(jí)組,比如加法優(yōu)先級(jí)(AdditionPrecedence) 和乘法優(yōu)先級(jí) (MultiplicationPrecedence) 等,你可以在這里找到完整的列表。如果沒(méi)有適合你的運(yùn)算符的優(yōu)先級(jí)組,你就需要像我們?cè)诶又凶龅眠@樣,自己指定結(jié)合律方式和優(yōu)先級(jí)順序了。

associativity

定義了結(jié)合律,即如果多個(gè)同類的操作符順序出現(xiàn)的計(jì)算順序。比如常見(jiàn)的加法和減法都是 left,就是說(shuō)多個(gè)加法同時(shí)出現(xiàn)時(shí)按照從左往右的順序計(jì)算 (因?yàn)榧臃M足交換律,所以這個(gè)順序無(wú)所謂,但是減法的話計(jì)算順序就很重要了)。點(diǎn)乘的結(jié)果是一個(gè) Double,不再會(huì)和其他點(diǎn)乘結(jié)合使用,所以這里是 none;

higherThan

運(yùn)算的優(yōu)先級(jí),點(diǎn)積運(yùn)算是優(yōu)先于乘法運(yùn)算的。除了 higherThan,也支持使用 lowerThan 來(lái)指定優(yōu)先級(jí)低于某個(gè)其他組。

infix

運(yùn)算的優(yōu)先級(jí),點(diǎn)積運(yùn)算是優(yōu)先于乘法運(yùn)算的。除了 higherThan,也支持使用 lowerThan 來(lái)指定優(yōu)先級(jí)低于某個(gè)其他組。

有了這些之后,我們就可以很簡(jiǎn)單地進(jìn)行向量的點(diǎn)積運(yùn)算了:

let result = v1 +* v2
// 輸出為 14.0”

最后需要多提一點(diǎn)的是,Swift 的操作符是不能定義在局部域中的,因?yàn)橹辽贂?huì)希望在能在全局范圍使用你的操作符,否則操作符也就失去意義了。另外,來(lái)自不同 module 的操作符是有可能沖突的,這對(duì)于庫(kù)開(kāi)發(fā)者來(lái)說(shuō)是需要特別注意的地方。如果庫(kù)中的操作符沖突的話,使用者是無(wú)法像解決類型名沖突那樣通過(guò)指定庫(kù)名字來(lái)進(jìn)行調(diào)用的。因此在重載或者自定義操作符時(shí),應(yīng)當(dāng)盡量將其作為其他某個(gè)方法的 "簡(jiǎn)便寫法",而避免在其中實(shí)現(xiàn)大量邏輯或者提供獨(dú)一無(wú)二的功能。這樣即使出現(xiàn)了沖突,使用者也還可以通過(guò)方法名調(diào)用的方式使用你的庫(kù)。運(yùn)算符的命名也應(yīng)當(dāng)盡量明了,避免歧義和可能的誤解。因?yàn)橐粋€(gè)不被公認(rèn)的操作符是存在沖突風(fēng)險(xiǎn)和理解難度的,所以我們不應(yīng)該濫用這個(gè)特性。在使用重載或者自定義操作符時(shí),請(qǐng)先再三權(quán)衡斟酌,你或者你的用戶是否真的需要這個(gè)操作符。

最后編輯于
?著作權(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)容