聲明:文章開(kāi)頭部分內(nèi)容翻譯自objc的一篇博客米酬。當(dāng)然赃额,我并沒(méi)有逐行翻譯原文阁簸,只是說(shuō)個(gè)大致意思启妹,順帶闡述一些自己的理解和擴(kuò)展思考醉旦,還有我自己的代碼车胡。
分解序列:
//分解成首元素和剩余數(shù)組
extension Array {
var decompose : (head: Element, tail: [Element])? {
return (count > 0) ? (self[0], Array(self[1..<count])) : nil
}
}
上面這個(gè)代碼片段是對(duì)Array
的一個(gè)擴(kuò)展(對(duì)extension
不熟悉對(duì)同學(xué)可以看看我以前的一篇文章)。decompose
作為擴(kuò)展的計(jì)算屬性丧慈,返回一個(gè)可空元組(Tuple?)逃默,元組包含數(shù)組的首元素和一個(gè)由剩余元素組成的數(shù)組簇搅,如果數(shù)組為空則返回nil
。這個(gè)分解操作配合if let
和模式匹配將非常好用吟税。譬如對(duì)序列數(shù)據(jù)的累加操作sum
:
//累加
func sum(list: [Int]) -> Int {
if let (head, tail) = list.decompose {
return head + sum(tail)
} else {
return 0
}
}
let sumResult = sum([1, 2, 3, 4]) //10
在函數(shù)式編程的世界中,取序列的首元素和剩余序列是一個(gè)很重要的操作肖抱,許多高階的序列操作都可以基于這個(gè)操作完成虐沥。上面說(shuō)了累加,稍微擴(kuò)展一下欲险,累乘呢天试?當(dāng)然也可以喜每。甚至我們可以用它定義一個(gè)更抽象更一般化的函數(shù)雳攘,功能與Swift提供的全局函數(shù)reduce
相同:
//山寨reduce
func reduce<T>(list: [T], initValue: T, function: (lhs: T, rhs: T) -> T) -> T {
if let (head, tail) = list.decompose {
return function(lhs: head, rhs: reduce(tail, initValue: initValue, function: function))
} else {
return initValue
}
}
let multiResult = reduce([2, 3, 5], initValue: 1, function: *) //30
reduce
接收三個(gè)參數(shù):序列list
、初始值initValue
刚照、操作函數(shù)function
无畔。我以multiResult
為例稍微講解一下這個(gè)函數(shù)的過(guò)程吠冤。這個(gè)函數(shù)的重點(diǎn)當(dāng)然是遞歸,事實(shí)上我認(rèn)為遞歸可以說(shuō)是函數(shù)式編程這種范式的核心之一郭变。
運(yùn)行reduce([2, 3, 5], initValue: 1, function: *)
诉濒,先是遞歸分解:
- [2, 3, 5]被分解為元組(2, [3, 5])遭赂。
- 2和
reduce([3, 5], initValue: 1, function: *)
的返回值將作為乘法操作*
的兩個(gè)參數(shù)。 - 執(zhí)行
reduce([3, 5], initValue: 1, function: *)
茄猫,[3, 5]被分解成元組(3, [5])。 - 3和
reduce([5], initValue: 1, function: *)
的返回值將作為乘法的左右因數(shù)相乘划纽。 - 執(zhí)行
reduce([5], initValue: 1, function: *)
勇劣,[5]被分解成元組(5, [])比默。 - 5和
reduce([], initValue: 1, function: *)
的返回值將作為乘法的左右因數(shù)相乘,而[]是個(gè)空數(shù)組篡九,它的decompose
屬性返回nil
榛臼,所以執(zhí)行else
之后的代碼塊沛善,即返回initValue
1。
至此向下遞歸結(jié)束金刁,接下來(lái)就是延遞歸棧往上計(jì)算各個(gè)function
函數(shù)(此例中即乘法)的值胀葱。最終得到1 * 5 * 3 * 2 = 30笙蒙。
當(dāng)然捅位,遞歸會(huì)消耗椡Р螅空間焰雕,如果遞歸很深的話芳杏,很有可能會(huì)導(dǎo)致棧溢出辟宗。有一種常見(jiàn)的優(yōu)化方式是尾遞歸(簡(jiǎn)單來(lái)說(shuō)泊脐,即把遞歸調(diào)用放到函數(shù)最后)容客,如果編譯器支持尾遞歸優(yōu)化的話缩挑,就會(huì)把函數(shù)中的一些中間變量舍去甚至直接優(yōu)化成循環(huán)形式鬓梅。具體內(nèi)容我就不展開(kāi)來(lái),大家可以看一下老趙早期的一篇《淺談尾遞歸的優(yōu)化方式》士袄,想必會(huì)有所得娄柳。
以sum
函數(shù)為例的話艘绍,進(jìn)行尾遞歸優(yōu)化我們可以將其改寫(xiě)為如下形式:
//累加
func sum(list: [Int]) -> Int {
guard let (head, tail) = list.decompose else {
return 0
}
return head + sum(tail)
}
新的sum
函數(shù)使用Swift2的新特性guard
進(jìn)行提前返回诱鞠,guard
是我很喜歡的一個(gè)語(yǔ)法航夺,哪怕不是為了尾遞歸優(yōu)化阳掐,我也推薦大家使用guard
語(yǔ)句處理邊界條件然后提前返回,這也是所謂的防御式編程中所提倡的缭保,我之前的一篇文章也有提到诸老。
最后再說(shuō)一個(gè)用到decompose
的快排函數(shù)吧:
//快排
func quickSort(list: [Int]) -> [Int] {
guard let (pivot, rest) = list.decompose else {
return []
}
let lesser = rest.filter { $0 < pivot }
let greater = rest.filter { $0 >= pivot }
return quickSort(lesser) + [pivot] + quickSort(greater)
}
可以看到這個(gè)函數(shù)抽象度很高钳恕,而且思路非常清晰——選取首元素作為參照點(diǎn),比參照點(diǎn)小的放參照點(diǎn)左邊畸肆,大的放右邊宦芦。函數(shù)的大致過(guò)程為:遞歸進(jìn)行分解排序,最后延遞歸棧向上連接數(shù)組轴脐。之前我寫(xiě)過(guò)一篇快排的文章调卑,里面的函數(shù)遠(yuǎn)沒(méi)有上面這個(gè)版本簡(jiǎn)潔優(yōu)雅。
快把decompose
加入你的Code Snippet中吧^ ^大咱。