Swift 中函数式编程的应用

作者: 诸葛俊伟 | 来源:发表于2016-07-22 06:15 被阅读132次

最近停下了 iOS 的学习,一心刷题。以前挺反感这种中国高考刷数学试卷式的应试方式,但是因为美国找工作只考算法,所以。。。不过,现在感觉刷过之后基础确实扎实了很多。我没有看过喵神的函数式 Swift,我下面所讲的只是函数式编程的冰山一角,有时间还是看看喵神的书为好。

刷题过程中用到最多的语句应该就是 for 循环吧?用到最多的结构应该就是数组(Array),链表(Linked List)和字典(Dictionary,在其它语言中应该是 hash map)。将 for 循环和 Array 或者 Dictionary 结合起来,就是 Swift 中的 Map, Filter 和 Reduce,还有 FlatMap,当然基础就是 Closure。我之前写过一篇非常详细完整的 Closure 文章,对 Closure 不太熟悉的童鞋可以点击传送门

Map

Map 就是遍历整个集合,对每个元素执行同样的操作,返回一个包含了对每个元素操作之后的数组

对上图中的操作,我们可以用 for 循环语言:

let val = [1,2,3,4]
var result = [Int]()
for i in val {
    result.append(i*i)
}
print("\(result)")

上面的代码看上去很平常,别的语言中几乎都这么写,但是在 Swift 中有更 Swifty 的方式,就是用 Map:

let val = [1,2,3,4]
let doubleVal = val.map{ $0 * $0 }

这里的 { $0 * $0 } 就是典型的 Closure,是最简化的版本,最初的是下面这样的。如何化简的这里就不多说了。

let doubleVal = val.map({
    (val: Int) -> Int in
    return val * val
})

结果中的数据类型并不一定等于原数据的数据类型,比如:

let scores = [0,28,124]
let words = scores.map { NSNumberFormatter.localizedStringFromNumber($0,
    numberStyle: .SpellOutStyle) }
// ["zero", "twenty-eight", "one hundred twenty-four"]

Map 操作还可以应用到 Dictionary 或者 set 上,但是输出的结果一定是 Array。比如:

let milesToPoint = ["point1":120.0,"point2":50.0,"point3":70.0]
let kmToPoint = milesToPoint.map { name,miles in miles * 1.6093 }
// [80.465, 112.651, 193.116]

Filter

Filter 的功能和字面意思一样,就是过滤的作用,返回一个满足条件语句的数组

Filter 也是用 Closure 来实现对原集合中各元素的判断,图中的示例代码如下:

let digits = [1,4,15,20]
let even = digits.filter { $0 & 0b1 == 0 }
// [4, 20]

Reduce

Reduce 就是对集合中的每个元素执行某种操作,附加(combine)到一个初始数值(initial value)上。

Reduce 中有两个变量,一个是初始数值,一个是 combine 的 closure。比如将数组中的所有数都乘到一个初始数值(这里为 1)上:

let digits = [1,2,3,4]
let even = digits.reduce(1, combine: *)
// 24

Swift 中的 String 是可以用 + 运算符的。所以还可以这样:

let hello = ["Love","U"]
let hi = hello.reduce("I", combine: +)
// "ILoveU"

根据尾部闭包(Trailing Closures)的定义,如果闭包是函数的最后一个参数,可以把闭包体写在圆括号的外面。所以在这里我们可以将 reduce(initial: T, combine: (T, String) throws -> T) 中的 combine closure 移到外面,比如:

let hello = ["Love","U"]
let hi = hello.reduce("I"){ "\($0) \($1)" }
// "I Love U",这样更好看吧

FlatMap

FlatMap 的一个简单应用就是将二维数组转换成一维数组:

let hello = [[1,2,3],[4,5,6]]
let hi = hello.flatMap{ $0 }
[1, 2, 3, 4, 5, 6]

还可以用来去除 optional nil,

let hello: [Int?] = [1,2,nil]
let hi = hello.flatMap{ $0 }
// [1, 2]

FlatMap 真正强大之处是可以很方便地对二维数组执行以前用两个 for 循环才能完成的操作。注意 flatMap 的 closure 中的变量是二维数组的第二维数组,所以我们可以将 filter,map,reduce 这些操作写在 flatmap 中 。比如:

let collections = [[1,2,3],[4,5,6]]
let onlyEven = collections.flatMap { $0.filter{ $0 & 0b1 == 0 } }
// [2, 4, 6]

Chaining

我们可以用链式的方式执行以上的那些操作。比如我们要将一个数组中大于0的数乘以2,我们可以先选出大于0的数 (Filter),然后再乘以2 (Map).

let digits = [1,2,-3,-4,5,0]
let result = digits.filter{ $0 > 0 }.map{ $0 * 2 }
// [2, 4, 10]

LeetCode 实战

最后我再分享一个实战的代码,是 LeetCode 中的 169. Majority Element,题目不难,方法可以有很多,在这个示例中我用的是 Bit Manipulation 方法,具体我就不详细解释了,有兴趣的可以去做一做。

func majorityElement(nums: [Int]) -> Int {
    let numBits = sizeof(Int) * 8
    let bitCounts = (0 ..< numBits).map { i in
        nums.reduce(0) { $0 + ($1 >> i) & 1 }
    }
    let major = (0 ..< numBits).reduce(0) {
        $0 | (bitCounts[$1] > nums.count/2 ? 1 << $1 : 0)
    }
    return major
}

欢迎转载,转载请注明出处。)

相关文章

网友评论

    本文标题:Swift 中函数式编程的应用

    本文链接:https://www.haomeiwen.com/subject/jnfijttx.html