Swift 程式語言

學習Swift程式語言:運算子重載(Operator Overloading)簡介

學習Swift程式語言:運算子重載(Operator Overloading)簡介
學習Swift程式語言:運算子重載(Operator Overloading)簡介
In: Swift 程式語言

好了,我們說的夠多了,讓我們來看看運算子超載(Operator Overloading)是怎麼一回事吧。

挑戰

這次的教學單的任務非常的容易:擴充乘法運算符的數量為標準功能。你將會用它來進行字串的聯級運算(concatenation operator),但過程卻比想像中的簡單。所以你可以想像我們將完成下面的事情:

"abc" * 5 = "abc" + "abc" + "abc" + "abc" + "abc" = "abcabcabcabcabc"

在開始埋頭苦幹的撰寫程式碼之前,你可以先停下來想想看你會怎麼來解決這個問題?你會把他分成哪幾個步驟呢?當然,下面是我的做法:

  • 建立結果變數(result variable) 並賦予它初始值- 預設的字串。
  • 建立一個從2開始的迴圈,並且在函式接受到的數字做結束,每個迴圈的執行週期只做一件事情– 把字串加到結果變數上。
  • 將結果顯示到Xcode的console上.

好了,這就是我構思的演算法-我們接著進入到實作的部分吧。

基本的運算子重載(Basic Operator Overloading)

learn-operator-overloading

首先,我們開啟Xcode 並且開啟playground模式。刪除playground模式下所有的內容,並且新增乘法運算函數的原形:

func *(lhs: String, rhs: Int) -> String {

}

我們剛剛建立的函數有兩個參數 – 左邊參數的型態為String 右邊參數的型態是 Int -最後我們回傳String型態並回傳乘法的執行結果。

有三件事情,你應該在在函式(function)的本體裡面完成。首先,建立結果變數(result variable)並賦予它初始值(initial value)-函式內的String是可變的-你很快就會需要來變動這個值了:

var result = lhs

接下來我們執行迴圈,這個迴圈將從2開始執行,一執行到跟函式內接收到的Int數值為止才結束,我們將使用for in的語句來實現流程控制與範圍操作的功能實現:

for _ in 2...rhs {

}

注意: 我們使用了通配符模式(Wildcard Pattern)來實作這部分的程式。通配符模式匹配並忽略任何值,包含一個底線(_)。當你不關心被匹配的值時,可以使用此模式。更多關參考的資料可以參考 這裡.

只有一件事你應該在Loop循環中執行-更新結果到結果變數(result variable)的字串變數中:

result += lhs

注意: 你也可以把上面的函示改成下面的程式來實作 –我們上面的程式碼會比較簡潔,因為我們採用加法賦值運算符(addition assignment operator)的模式。(譯者註:看來這一章節,原作者想讓大家有更多收穫,教我們很多東西呢,不熟悉加法賦值運算符的同學們可以多多學習喔。):

result = result + lhs

終於,到了回傳變數的時候到了。

return result

好了,現在我們來使用我們剛剛完成的運算符吧:

let u = "abc"
let v = u * 5

好了,就是這樣!這邊只剩下一個問題,我們只能針對字串做乘法運算。那要怎麼實現其他的類型呢?(好比說加法)所以,我們接著使用泛型運算子 (generic operators)來解決這個問題

泛型運算子(Generic Operators)

generic-operators

通用型別 (Generic Types) 預設不被我們的運算函示所支援,所以我們需要一個為她來建立一個協定型別(protocol Type) 。我們在playground下面添加這樣的協定型別(protocol Type):

protocol Type {

}

現在,我們已經為為先的指派運算子(assignment operator)函數加上了協定(protocol):

func +=(inout lhs: Self, rhs: Self)

這個函式擁有左邊跟右邊兩個型態為self的運算元-這是一種很特別的方式來敘述任何型別的資料都可以在協定(protocol)裡面實現。左邊的運算元被標示為inout 寫入讀出(in-out)參數,因為他的值會被修改而且會從函式裡面回傳。

另外,你也可以定義加法運算符函數的原型:

func +(lhs: Self, rhs: Self) -> Self

這個函式也擁有左邊跟右邊兩個型態為self的運算元,並且返回相加結果作為作為類型Self的值。在這種情況下,你並不需要使用inout參數了。

下一步,我們來建立String,Int,DoubleFloat等類型的擴充來實現協定型別(protocol Type):

extension String: Type {}
extension Int: Type {}
extension Double: Type {}
extension Float: Type {}

注意:因為你不希望任何東西添加到默認類型擴展的實現都是空的。您只需讓他們為了遵守協議。

現在添加乘法運算符函數的原型到playground檔案中:

func *(lhs: T, rhs: Int) -> T {

}

這個函數有有兩個參數-左邊的運算元的類型是T右手邊的運算元的類型是Int-並返回乘法結果的值類型為T。您可以使用類型約束,使泛型類型T符合類型協議Type( Type protocol),所以現在明白加法賦值運算了嗎?

注意: 你可以定義類型約束來替代使用關鍵字的方法-雖然我們原先的做法比較簡潔:

func *(lhs: T, rhs: Int) -> T

該功能的實現是跟我們先前的情況是一模一樣的:

var result = lhs

for _ in 2...rhs {

    result += lhs
    
}

return result

注意: 你可以加法運算來替代上面的功能-請確保在這個case中,它需要被添加在函式的prototype到protocol內,:.

現在我們來看看泛型運算子(generic operators)的運作狀況吧:

let x = "abc"
let y = x * 5

let a = 2
let b = a * 5

let c = 3.14
let d = c * 5

let e: Float = 4.56
let f = e * 5

好了,就是這樣!這邊只剩下一個問題:你使用的是標準的乘法運算符。也許你會感到有一點困惑。如果我們將它改成其他的操作方式會變得更好。

好吧,我們來看看我們怎麼來我們怎麼將它改造成客制化的運算子。

客制化的運算子

custom-operators

把下面的程式碼添加到playground檔案裡面去,並開始這方面的教學吧。

infix operator ** {associativity left precedence 150}

這裡到底發生了怎麼事情呢,我們一步一步的來說明它:

  1. 我們客製化的乘法運算符號的名字是`**`.
  2. 它的類型是`infix` 因為他是二進位的運算使用兩個運算子。
  3. 它將會由左至右來進行運算,所以他的結合方向(associativity)是往左邊。
  4. 它的優先權是`150` – 同層級的優先權是標準的乘法計算,因為他是比較高優先權的運算子。

注意:這邊有更多關於運算子的優先級別結合方向的相關資料。

客製化運算子的函數原型跟我們標準版的非常相近-只有名字不一樣:

func **(lhs: T, rhs: Int) -> T {

}

這個函式的實作也跟先前的一模一樣:

var result = lhs

for _ in 2...rhs {

    result += lhs
    
}

return result

我們來試看看客製化運算子的威力吧:

let g = "abc"
let h = g ** 5

let i = 2
let j = i ** 5

let k = 3.14
let l = k ** 5

let m: Float = 4.56
let n = m ** 5

好了,就這樣!我們只剩下一個問題-這個運算子的複合版本到現在還沒定義,我們將在下一個章節解決這樣的問題。

複合運算子(compound operator)

compound-operators

這個複合運算子的類型,優先層級和結合方向跟我們先前提到的案例一樣-只有命名不同

infix operator **= {associativity left precedence 150}

接著我們添加複合運算子(compound operator)的原型函式在(playground)內:

func **=(inout lhs: T, rhs: Int) {

}

這個函數沒有返回類型,因為他左邊的運算元inout

只有一件事情你應該在函式的本體裡面完成-使用我們先前已經建構好的客製化的運算子定義來回傳乘法結果:

lhs = lhs ** rhs

現在,讓我們來測試我們建構好的運算子吧:

var o = "abc"
o **= 5

var q = 2
q **= 5

var s = 3.14
s **= 5

var w: Float = 4.56
w **= 5

好了,沒有其他更簡單的方式了!

恭喜你

運算子重載,再謹慎地使用下,可以非常的強大-我希望你可以方法可以讓他在你專案中被使用到。

作為參考,你可以 下載Playground 檔案在GitHub上。我已經在Xcode 7.3 跟Swift 2.2的環境中測試過了。

如果你對於這個教學課程或以及運算子重載(operator overloading)有任何見解,請在下面留言並分享你的想法。

Credit: 本文內使用到的漫畫由MakeBeliefsComix.com.所設計的
譯者簡介:黃凱揚-系統分析與資深程式設計師,畢業南台科技大學電子系,曾在人力資源相關領域進行系統開發,13年的系統開發整合經驗,近年來協助客戶行 App 軟體以及 WebBase系統開發與系統分析與整合。

原文An Introduction to Operator Overloading

作者
Cosmin Pupăză
Cosmic Pupăză 在部落格 cosminpupaza.wordpress.com 分享有關 Swift 和 iOS 開發文章,以及參與了 raywenderlich.com 和 appcoda.com 兩個平台的 Swift 教學團隊。平日喜歡玩結他和研究二戰歷史。可以透過電郵、Facebook或 Twitter 找到 Cosmic。
評論
更多來自 AppCoda 中文版
很好! 你已成功註冊。
歡迎回來! 你已成功登入。
你已成功訂閱 AppCoda 中文版 電子報。
你的連結已失效。
成功! 請檢查你的電子郵件以獲取用於登入的連結。
好! 你的付費資料已更新。
你的付費方式並未更新。