最近停下了 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
}
欢迎转载,转载请注明出处。)
网友评论