通过Swift学函数式编程

2018-05-28 13:39:41 浏览数 (1)

在文章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


  1. http://leverich.github.io/swiftislikescala/ ↩︎
  2. http://www.raywenderlich.com/82599/swift-functional-programming-tutorial ↩︎
  3. http://jamesonquave.com/blog/functional-programming-in-swift/ ↩︎
  4. https://developer.apple.com/videos/wwdc/2014/#404 ↩︎

0 人点赞