重讀 Swift 之二:Operator Declaration(運算子過載)

發表於2016-10-03

一、為什麼要過載運算子


我們都知道所謂的運算子平常的也就是 + - * / 之類的,比如我們隨便寫個 1 + 2 列印肯定輸出的是 3 ,那麼我們為什麼還要過載運算子呢?下面我們就舉個例子,如下我定義個結構體

然後我們定義兩個變數 V1,V2

然後我們來寫V1 + V2,報錯

11571495-ed690fe1f87cda52

error

報錯 + 不能用於 Vector,這是因為 Vector 使我們自定義的一個結構體,所以編譯器不知道我們要用 + 對這個結構體做什麼操作。因此,在這裡我們就需要用到運算子的過載。

二、如何過載運算子


  • 1、含有兩個引數的運算子的過載
    因為運算子是一個函式,比如對於陣列的 reduce方法我們就可以如下

    結果是 10 ,這裡的 +就代表了一個函式,所以我們重新寫的時候可以如下

    這樣我們就實現了 + 的過載,上面函式中 left 和 right 兩個引數都是 Vector 型別的,一個表示運算子左邊的引數一個表示運算子右邊的引數(這兩個引數是有先後順序的,由於加法滿足加法交換律所以這裡體現不出來,有興趣的可以試一下 - 的過載,這時候就要注意順序了),然後返回值得型別也是 Vector 型別,再來實現V1 + V2的時候,就發現得到了一個新的 Vector 型別的值

12571495-7743fd3f2d5a3a4e

V1 + V2

這裡我們就已經完成了 + 這個運算子的過載。當然有興趣的童鞋還可以試著自己實現 - 或者 * 的過載,這裡就不一一舉例了。
關於雙目運算子的過載,和單目運算子類似,如下

執行結果

13571495-407bf897c0229ca7

V1
  • 2、含有一個引數的運算子的過載
    照著上面單目運算子的方式我們自己來寫個 - 過載例子,如下

    按照我們的邏輯這裡取反邏輯上應該是正確的,可是編譯會發現報錯

14571495-5e066ede422ddd2e

error

這裡我們就要注意了,和有兩個引數的運算子不同的是,只有一個引數的運算子位置是不固定的,這裡的 - 可以在前可以在後,所以我們在這裡還需要注意運算子的位置

這裡加上一個 prefix 表示前置(後置是 postfix)。這樣就可以明確運算子的位置

15571495-40e8c6d074f5ebea

-V1
  • 3 、比較運算子的過載
    關於比較運算子的過載,顧名思義也是有兩個引數的,返回值肯定是個 Bool 型別的,如下過載 == 運算子

16571495-2d71c68a6b38af31

V1 與 V2 的比較

再來看看 > 的過載,邏輯稍微多一點

此時再去比較 V1 和 V2 就會出現你邏輯中的效果。常規的運算子就說到這裡,下面我們來看一下自定義運算子的過載。

Tips:對於運算子的過載我們是不能過載 = 的,它是被編譯器固定擁有的,在底層它是與記憶體管理相關的,,我們不能認為的去改變它,這裡需要注意一下(或者你可以別把賦值運算 = 看成運算子 +_+)。
注意以下這些標記=->///**/.&??(中綴運算子)>(字尾運算子)!? 是被系統保留的。這些符號不能被過載,也不能用於自定義運算子。

三、自定義運算子的過載


上面我們所說的都是 Swift 中已經存在了的運算子,那麼我們能不能自己定義運算子呢?答案是肯定的,在文件中我們可以看到這麼一句話

意思就是

  • 1、自定義單目運算子
    所以我們在自定義運算子的時候要注意一下。下面我們就來簡單的自定義一個單目運算子 +++,這個運算子的作用呢就是讓 Vector 中的每個變數都加 1 ,如下

    但是編譯的時候會報錯,如下

    17571495-508132cfcf9a52a5

    error

    這是因為我們沒有明確的定義這個 +++,所以編譯器不識別。所以我們應該申明一下這個運算子,正確的程式碼如下

    在前面我們用 prefix operator +++ 宣告前置運算子 +++ ,這樣後面就可以用了

    如上輸出結果就是 Vector(x: 2, y: 2, z: 2),到此,單目運算子的自定義就完成了。

  • 2、自定義雙目運算子
    雙目運算子的定義和單目運算子的定義類似,但是雙目運算子自定義的時候的關鍵字是 infix,如下

    上面我們就自定義了一個求平方的雙目運算子 **,然後我們試試 2 ** 2就可以看到結果是 4.0
    上面好像沒有什麼問題了,下面我想算一個平方的平方,撥入 2 的 2次方的 3 次方,照著邏輯應該這樣寫

    但是編譯我們會發現報錯,如下

    18571495-b2e1e315590d0634

    error

    錯誤是說我們上面的運算是個非結合性的運算,所謂的結合性(associativity)就是運算的先後順序,在 Swift 2 中我們都知道還有個優先順序(precedence),預設的是 100 ,它的範圍是 0~200 ,這個是用來設定運算子優先順序的,比如在swift 2.2 中我們完全定義一個求平方運算子就是

    在 Swift 3 中有些變化,如下

    如上我們輸入 2 ** 2 ** 3,就會發現結果是 256.0,這是因為我們把 associativity 設定成為了 right,所以運算從右邊開始,先算 2 ** 3 = 8.0,然後再是 2 ** 8.0 = 256.0,如果我們把 associativity 設定成為了 left,就會發現結果是 64.0。關於更多的 associativity 和 higherThan 或者 lowerThan 之類的可以在下方參考連線中參考,這裡就不一一說明了。
    差不多運算子過載就到這裡了,如果還有什麼遺漏,歡迎大家指正!

參考:
1、swift-evolution
2、Operator Declaration

相關文章