在日常開發(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ù)列的新算法,原理是矩陣的乘法
根據(jù)矩陣相乘的結(jié)合率翎苫,我們可以不斷調(diào)用公式(1)來生成下一個斐波那契數(shù)权埠,即
如果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ù)
現(xiàn)在因為Matrix滿足monoid的定義呐粘,并且實現(xiàn)了相關(guān)操作满俗,求冪的方法也可以應(yīng)用之前的算法转捕,讓斐波那契的算法復(fù)雜度從O(n)降低到了O(logn)。