Swift 中函数式编程的应用

最近停下了 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
}

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • 前言 在swift中,对诸如Array, Dictionary集合类型使用map, filter, reduce进...
    herbsun阅读 1,314评论 0 7
  • 前言 本文翻译自Swift Functional Programming Tutorial 翻译的不对的地方还请...
    桃红宿雨阅读 2,635评论 0 11
  • 2017/3/27【打卡】13#杨春华第157天: 1.25#陈蕾,高效率的博学多才的善解人意的女神,先让我好好膜...
    春华秋实吧阅读 161评论 0 0
  • 随笔: 真是好久没有打篮球了,这两天关注NBA总决赛骑士VS勇士,作为一个伪球迷,我都看着揪心,忍不住邀朋友去球...
    夕影西逝阅读 124评论 0 0