protocol進化論 - swift

在日常開發(fā)中經(jīng)常會對接口進行調(diào)整用來適應(yīng)新的變化齐遵,這個過程可以說是一種進化氢卡。

這里將通過一個例子來說明protocol的定義和進化璃赡。

比如我要知道兩個整數(shù)的乘法羹令,a*b,記為a.times(b)
這樣把兩個數(shù)記為乘數(shù)和被乘數(shù)眷蜓,那么這個操作可以通過把a連續(xù)加b次即可

func times(_ a: Int, _ b: UInt) -> Int {
  return b==0 ? 0 : times(a, b-1) + a
}

現(xiàn)在得到了一個倍數(shù)函數(shù),可以計算整數(shù)的正整數(shù)倍,很好炕檩,如果a是double呢,是float呢,是string呢笛质,怎么辦泉沾,這個能夠怎么適應(yīng)這些變化,我們應(yīng)該怎么去提取這些公共的變化妇押,如果是double跷究,那么我們可以有:

func times(_ a: Double, _ b: UInt) -> Double {
  return b==0 ? 0 : times(a, b-1) + a
}

比較一下這兩個函數(shù),除了a和返回值的類型外都是一樣的敲霍,這種情況下俊马,泛型為我們帶來了強大的支持,我們來修改一下

func times<T>(_ a: T, _ b: UInt) -> T {
  return b==0 ? 0 : times(a, b-1) + a
  //compile error: binary operator '+' cannot be applied to two 'T' operands
}

結(jié)果怎樣肩杈,這段代碼給出了幾個錯誤柴我,首先是加法運算符不支持,因為編譯器不知道T的類型扩然,無法推斷出加法操作艘儒,在標(biāo)準(zhǔn)庫里面,Int和Double都實現(xiàn)了Numeric都protocol夫偶,其中聲明了加減乘都操作符界睁,下面修改一下代碼

func times<T:Numeric>(_ a: T, _ b: UInt) -> T {
  return b==0 ? 0 : times(a, b-1) + a
}

這段代碼工作的非常好,所有實現(xiàn)Numeric的類型都可以使用這個函數(shù)了
接下來我們可以考慮更多了兵拢,比如字符串在python里面有一個乘法操作非常高級翻斟,"123"*3=="123123123",這里我們嘗試給String也增加一個times方法说铃,剛才的實現(xiàn)明顯就不好使了杨赤,因為String明顯沒有實現(xiàn)Numeric的操作,如何找到一個既能適用于數(shù)字截汪,又能適用于String的操作呢疾牲,在這個例子里面我們需要一個包含加法操作的類型,但是String不支持衙解,我們嘗試自己聲明一個

protocol addable {
  static func + (lhs: Self, rhs: Self) -> Self
}
extension Int:addable {}
extension String:addable {}
func times<T:addable>(_ a: T, _ b: UInt) -> T {
  return b==0 ? 0 : times(a, b-1) + a
  //error: result values in '? :' expression have mismatching types 'Int' and 'T'
}

這次的錯誤比較明顯了阳柔,因為0不能轉(zhuǎn)換成T,只有Numeric的時候可以蚓峦,所以這里需要一個零值舌剂,對于字符串來講就是空字符串,這個我們需要加到protocol里面

protocol addableAndZero {
    static func + (lhs: Self, rhs: Self) -> Self
    static func zero() -> Self
}
extension Int:addableAndZero {
    static func zero() -> Int {return 0}
}
extension String:addableAndZero {
    static func zero() -> String {return ""}
}
func times<T:addableAndZero>(_ a: T, _ b: UInt) -> T {
    return b==0 ? T.zero() : times(a, b-1) + a
}

完美暑椰,這下我們可以擴充很多類型了霍转,這個暫時告一個小段落

不過addableAndZero這算什么名字,是不是太奇怪了一汽,是不是要拆分開成兩個protocol避消,從這個名字上來看拆分是合理的低滩,因為and表示兩者沒有太大關(guān)系。

標(biāo)準(zhǔn)化

我們平時研究的很多問題都可能是別人研究過岩喷,并形成了標(biāo)準(zhǔn)的方法和概念恕沫,我們在思考的問題的時候也要盡量考慮使用標(biāo)準(zhǔn)化的方式,這樣的好處是溝通比較方便纱意。比如說23中設(shè)計模式就是一種標(biāo)準(zhǔn)化婶溯,大家討論某個模式的時候不用專門把這個模式再講一遍,這就是標(biāo)準(zhǔn)的作用偷霉。

幺半群(monoid):表示一種集合迄委,其中定義了一種操作和一個幺元,操作可以應(yīng)用在群中任意兩個元素上类少,并且得到的結(jié)果也在群中跑筝,操作本身支持結(jié)合律,即(a*b)*c = a*(b*c)瞒滴,幺元也是群中的一個元素曲梗,其性質(zhì)為任意元素和幺元進行操作還等于該元素。

String就是一個標(biāo)準(zhǔn)的幺半群妓忍,支持連接操作虏两,幺元為空串,其性質(zhì)為:

  • 任意String連接在一起還是String世剖,也支持結(jié)合律定罢,和空字符串連接不變。

整數(shù)也是幺半群旁瘫,支持加法操作祖凫,幺元為0(也可定義乘法和1)

c++里的int也是幺半群

swift里面有些不一樣,因為大數(shù)相加溢出后會報錯酬凳,也就是不能操作惠况,這里我們暫且不考慮這種情況

這里給出幺半群的定義

protocol monoid {
    static func op(_ lhs: Self, _ rhs: Self) -> Self
    static func identity() -> Self
}

修改前面的定義

extension Int:monoid {
    static func identity() -> Int {return 0}
    static func op(_ lhs: Int, _ rhs: Int) -> Int {
        return lhs + rhs
    }
}
extension String:monoid {
    static func identity() -> String {return ""}
    static func op(_ lhs: String, _ rhs: String) -> String {
        return lhs + rhs
    }
}
func times<T:monoid>(_ a: T, _ b: UInt) -> T {
    return b==0 ? T.identity() : T.op(times(a, b-1), a)
}

接下來會有神奇的事情發(fā)生了,我們把Int的op定義為乘法宁仔,identity定義成1

extension Int:monoid {
    static func identity() -> Int {return 1}
    static func op(_ lhs: Int, _ rhs: Int) -> Int {
        return lhs * rhs
    }
}
times(2,6) == 64//2^6

我們得到了求冪的功能稠屠。

知識擴展

最后給一個斐波那契數(shù)列的新算法,原理是矩陣的乘法
\left\{ \begin{matrix} 1 & 1 \\ 1 & 0 \end{matrix} \right\} * \left\{ \begin{matrix} a \\ b \\ \end{matrix} \right\} = \left\{ \begin{matrix} a+b \\ a \\ \end{matrix} \right\} \tag{1} \\
根據(jù)矩陣相乘的結(jié)合率翎苫,我們可以不斷調(diào)用公式(1)來生成下一個斐波那契數(shù)权埠,即
\left\{ \begin{matrix} Fib(n) & Fib(n-1) \\ Fib(n-1) & Fib(n-2) \end{matrix} \right\} = \left\{ \begin{matrix} 1 & 1 \\ 1 & 0 \end{matrix} \right\} ^n * \left\{ \begin{matrix} Fib(0) \\ Fib(1) \\ \end{matrix} \right\} \tag{2}
如果a=1,b=1煎谍,那么方陣求冪后就是

我們簡單定義二元矩陣

struct Matrix2 {
    let a00 : Int, a01: Int, a10: Int, a11: Int
}
extension Matrix2:monoid {
    static func identity() -> Matrix2 {return Matrix2(a00:1, a01:0, a10:0, a11:1)}
    static func op(_ lhs: Matrix2, _ rhs: Matrix2) -> Matrix2 {
        return Matrix2(
            a00: lhs.a00 * rhs.a00 + lhs.a01 * rhs.a10,
            a01: lhs.a00 * rhs.a01 + lhs.a01 * rhs.a11,
            a10: lhs.a10 * rhs.a00 + lhs.a10 * rhs.a10,
            a11: lhs.a10 * rhs.a01 + lhs.a11 * rhs.a11
        )
    }
}
let kM = Matrix2(a00: 1, a01: 1, a10: 1, a11: 0)
let k10 = times(kM,10)

這樣把一些之前看起來無關(guān)的一些算法通過monoid概念居然實現(xiàn)了統(tǒng)一攘蔽,同時我們還可以對times的算法進行一些優(yōu)化,最簡單的優(yōu)化就是根據(jù)
times(n+m) = times(n) * times(m)

現(xiàn)在因為Matrix滿足monoid的定義呐粘,并且實現(xiàn)了相關(guān)操作满俗,求冪的方法也可以應(yīng)用之前的算法转捕,讓斐波那契的算法復(fù)雜度從O(n)降低到了O(logn)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末漫雷,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鳍咱,更是在濱河造成了極大的恐慌降盹,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谤辜,死亡現(xiàn)場離奇詭異蓄坏,居然都是意外死亡,警方通過查閱死者的電腦和手機丑念,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門涡戳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人脯倚,你說我怎么就攤上這事渔彰。” “怎么了推正?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵恍涂,是天一觀的道長。 經(jīng)常有香客問我植榕,道長再沧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任尊残,我火速辦了婚禮炒瘸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘寝衫。我一直安慰自己顷扩,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布慰毅。 她就那樣靜靜地躺著屎即,像睡著了一般。 火紅的嫁衣襯著肌膚如雪事富。 梳的紋絲不亂的頭發(fā)上技俐,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天,我揣著相機與錄音统台,去河邊找鬼雕擂。 笑死,一個胖子當(dāng)著我的面吹牛贱勃,可吹牛的內(nèi)容都是我干的井赌。 我是一名探鬼主播谤逼,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼仇穗!你這毒婦竟也來了流部?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤纹坐,失蹤者是張志新(化名)和其女友劉穎枝冀,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體耘子,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡果漾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了谷誓。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绒障。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖捍歪,靈堂內(nèi)的尸體忽然破棺而出户辱,到底是詐尸還是另有隱情,我是刑警寧澤糙臼,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布焕妙,位于F島的核電站,受9級特大地震影響弓摘,放射性物質(zhì)發(fā)生泄漏焚鹊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一韧献、第九天 我趴在偏房一處隱蔽的房頂上張望末患。 院中可真熱鬧,春花似錦锤窑、人聲如沸璧针。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽探橱。三九已至,卻和暖如春绘证,著一層夾襖步出監(jiān)牢的瞬間隧膏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工嚷那, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留胞枕,地道東北人。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓魏宽,卻偏偏與公主長得像腐泻,于是被迫代替她去往敵國和親决乎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容