在文章SWIFT IS A LOT LIKE SCALA [1] 提到Swift和Scala有很大的相似之处,在某些特性甚至比Scala对函数式编程的支持更友好。笔者遂从Swift语言出发,学习函数式编程[2] [3],并记录笔记如下。
什么是函式编程
是一种编程范式,强调数学化的函数,不可变(immutable),expressiveness,尽量少地使用变量、状态。
由这些特性带来的好处有:解耦,使App易于测试,易于并发、并行化。
Map Reduce Filter
Array Filter
这些函式方法和Python很像,例子:
代码语言:javascript复制func isEven(number: Int) -> Bool {
return number % 2 == 0
}
// First-class function, 函数作为变量看待
let evens1 = Array(1...10).filter(isEven)
println(evens1)
// Closure 闭包,匿名函数,即Python里面的Lambda
let evens2 = Array(1...10).filter{
(number) in
number % 2 == 0
}
自制Filter
此处自定义了一个泛型函数Generic Function。
代码语言:javascript复制func myFilter<T>(source: [T], predicate:(T) -> Bool) -> [T] {
var result = [T]()
for i in source {
if predicate(i) {
result.append(i)
}
}
return result
}
Array Reduce
任务:取1…10的偶数求和
代码语言:javascript复制let evenSum = Array(1...10)
.filter { (number) in number % 2 == 0 }
.reduce(0) { (total, number) in total number }
或者简写
代码语言:javascript复制let evenSum = Array(1...10)
.filter { $0 % 2 == 0 }
.reduce(0) { $0 $1 }
println(evenSum)
自定义Reduce
代码语言:javascript复制extension Array {
func myReduce<T,U>(seed:U, combiner:(U,T)->U) -> U
{
var cur = seed
for item in self {
cur = combiner(cur, item as T)
}
return cur
}
let evenSum = Array(1...10)
.filter { (number) in number % 2 == 0 }
.myReduce(0) { (total, number) in total number }
println(evenSum)
递归Reduce
需先声明Closure类型,再定义行为。参考Advanced Swift at WWDC14 [4] 的视频。
代码语言:javascript复制let list: ArraySlice = [1,2,3,4,5,6,7,8,9,10]
var closure:((Int, ArraySlice<Int>) -> Int)!
closure = { (memo, list) in
if (list.count == 0) {
closure = nil // remove retain cycle
return memo
} else {
return closure(memo list[0], list[1..<list.count])
}
}
let value = closure(0, list)
函数式编程练习:建立字典索引
输入如:
let words = ["Guinea Pig", "Mouse", "Monkey", "Fish", "Dog", "cat", "chicken"]
求输出:
代码语言:javascript复制let dict = [(C, [Cat, Chicken]),
(F, [fish]),
(D, [Dog]),
(M, [Mouse, monkey]),
(G, [Guinea Pig])]
思路1:
代码语言:javascript复制extension String {
subscript (i: Int) -> Character {
return self[advance(self.startIndex, i)]
}
func firstCharacter() -> Character {
return self[0] as Character
}
}
typealias WordList = [String]
typealias Entry = (Character, WordList)
typealias Entries = Array<Entry>
func buildIndexOf(words: WordList) -> Entries {
return words
.reduce(Entries()) { (entries, word) in
// 1
// for each word in words
// check if word.firstChar in entries
for entry: Entry in entries as Entries {
let char: Character = entry.0
let word_list: WordList = entry.1
// 2
// Indexed char: add word to word_list
if char == word.uppercaseString.firstCharacter() {
let new_entry = Entry(char, word_list [word])
// immutable entries, need to create new by append
return entries.filter() {$0.0 != char } [new_entry]
}
}
// 3
// Unindexed char: create Entry
let entry: Entry = Entry(word.uppercaseString.firstCharacter(), [word])
return entries [entry]
}
}
let words = ["Guinea Pig", "Mouse", "Monkey",
"Fish", "Dog", "cat", "chicken"]
println(buildIndexOf(words))
思路2:(参见[2] )
代码语言:javascript复制func buildIndexOf(words: WordList) -> Entries {
let distinctIndex: [Character] = words.reduce([Character]()) {
(char, word) in
if !contains(char, word.uppercaseString.firstCharacter()) {
return char [word.uppercaseString.firstCharacter()]
}
return char
}
// distinctIndex = ['A','F','G',...]
// add word into index
let entries: Entries = distinctIndex.map {
char -> Entry in // -> Entry
return Entry(char, words.filter() { char == $0.uppercaseString.firstCharacter() })
}
return entries
}
let words = ["Guinea Pig", "Mouse", "Monkey",
"Fish", "Dog", "cat", "chicken"]
println(buildIndexOf(words))
尾递归 Tail-Recursion
- http://leverich.github.io/swiftislikescala/ ↩︎
- http://www.raywenderlich.com/82599/swift-functional-programming-tutorial ↩︎
- http://jamesonquave.com/blog/functional-programming-in-swift/ ↩︎
- https://developer.apple.com/videos/wwdc/2014/#404 ↩︎