赞
踩
- 原文地址:Intro to Swift Functional Programming with Bob
- 原文作者:Bob Lee
- 译文出自:掘金翻译计划
- 译者:Deepmissea
- 校对者:thanksdanny,Germxu
老司机怎么开车,我们就怎么开
你懂的。很多人都讨论它。你 Google 一下然后看了看前五篇文章,令人沮丧的是,你发现大部分文章只给出一个含糊不清的 Wikipedia 定义,像是:
“函数式编程是一种编程范式,能让你的代码清晰又明确,没有变量也没有状态。”
和你一样,老兄,事实上,我也这样搜索过。我温柔地做了个捂脸的表情,并轻声回应道:
这 TM 是啥?
和闭包很像。如果你不理解什么是后进和关键标识,比如 $0,那你还没做好阅读这篇教程的准备。你可以暂时离开,找这里的资源来升升级。
我是十万个为什么。为什么要学习函数式编程?好吧,最好的答案往往来自于历史。假设你要创建一个添加一个数组的计算器应用。
- // Somewhere in ViewController
-
- let numbers = [1, 2, 3]
- var sum = 0
-
- for number in numbers {
- sum += number
- }
没问题,但是如果我再添加一个呢?
- // Somewhere in NextViewController
-
- let newNumbers = [4, 5, 6]
- var newSum = 0
-
- for newNumber in numbers {
- newSum += newNumber
- }
这看起来就像我们重复我们自己,又长又无聊,还没必要。你不得不创建一个 sum
来记录添加的结果。这很让人崩溃,五行代码。我们最好创建一个函数代替这些玩意。
- func saveMeFromMadness(elements: [Int]) -> Int {
- var sum = 0
-
- for element in elements {
- sum += element
- }
-
- return sum
- }
这样在需要使用 sum
的地方,直接调用
- // Somewhere in ViewController
- saveMeFromMadness(elements: [1, 2, 3])
-
- // Somewhere in NextViewController
- saveMeFromMadness(elements: [4, 5, 6])
停下别动,对。你现在已经尝试了一个函数式范式的使用。函数式就是用函数来得到你想要的结果。
在 Excel 或者 Google 的 Spreadsheet 上,如果要对一些值求和,你需要选择表格,然后调用一个像是 C# 编写的函数。
Excel 里的求和函数
好,就是这样,再见。感谢阅读。
最后,现在,是时候拿出详细的函数式编程定义了。
我们经常把函数式编程描述为声明式的。你无须在意这个答案从何而来。举个例子,一个人来爬珠穆朗玛峰,可能从飞机上跳下去,也可能花好几年从地下开始爬。你会得到同样的结果。人们往往不知道 Excel 表格里的 Sum
是怎么组成的,但是,它就是得到相应的结果。
一个残酷的例子,众所周知的非函数式编程,我们经常看到上面的调用被称为命令式。它告诉你如何(how)得到从 A 到 B 的答案。
- let numbers = [1, 2, 3]
- var sum = 0
-
- for number in numbers {
- sum += number
- }
人们知道你循环了 numbers
。但是,这有必要么?我不在意它是怎么做的,我只在意结果的出来的迅速快捷。
因此,Excel 和 Spreadsheet 融合了函数式编程的范式,来更快更简单的获取结果,而不需要关心具体的实现。(我父亲也没必要在处理公司财务数据的时候关心它)
在上面那个让人崩溃的例子里,我们不得不创建一个 var sum = 0
来跟踪每个视图控制器的增加结果。但是这有必要吗?sum
的值不断改变,如果我弄乱了总和怎么办?而且,我在10 条 tips 让你成为一个更好的 Swift 开发者中提到过,
更多的变量 → 更多记忆 → 更多头痛 → 更多 bug → 更多的生活问题。
更多的变量 → 容易 TM 的 → 完蛋
因此,函数式范式确保在使用的时候不可变或者没有状态的变化。
而且,和你意识到的或即将发现的一样,函数式范式提供了一个更利于维护代码的模型。
那好,现在你明白了为什么我们喜欢函数式编程。所以呢?嗯,这篇教程,只专注于基本面。我不会讨论函数式编程在事件和网络等等中的应用。我可能会发一些通过 RxSwift 来做这些事的教程。所以说如果你是新手,跟着我,螺旋稳。
你可能已经见过像 filter
、map
、reduce
等等的一些东西。不错,这些让你用函数式的途径中的过滤器来处理一个数组。确保你对泛型的理解同样的酷。
这全是关于基本面的东西。如果我能教你如何在泳池里游泳,那你也可以在海里,湖里,池塘里,泥坑里(最好不是)游泳,这这篇教程,如果你学会了基本面,你就可以创建你自己的 map
和 reduce
或者其他你想要的炫酷函数。你可以 google 东西,否则,你不会从我这里得到这个叫“Bob”的开发者的解释了。
假设你有个数组。
let recentGrade = ["A", "A", "A", "A", "B", "D"] // My College Grade
你想要过滤/带来并且返回一个只包含 “A” 的数组,这能让我妈妈感到快乐。你怎么用命令式的方式来做这个?
- var happyGrade: [String] = []
-
- for grade in recentGrade {
- if grade == "A" {
- happyGrade.append(grade)
- } else {
- print("Ma mama not happy")
- }
- }
-
- print(happyGrade) // ["A", "A", "A", "A"]
这简直让人发疯。我竟然写了这种代码。我不会在校对的时候重新检查,这很残忍。视图控制器中的8行代码?
不堪回首。
我们必须停止这种疯狂,并拯救所有像你这么做的人。让我们创建一个函数来完成它。振作起来。我们现在要对付一下闭包了。让我们试着创建一个过滤器来完成和上面一样的工作。真正麻烦现在来了。
现在我们创建一个函数,有一个包含 String
类型的数组并且有个闭包,类型是 (String) -> Bool
。最后,它返回一个过滤后的 String
数组。为啥?忍我两分钟就告诉你。
func stringFilter(array: [String], returnBool: (String) -> Bool) -> [String] {}
你可能会对 returnBool
部分特别苦恼。我知道你在想什么,
那么,我们要在返回 Bool 下传递什么?
你需要创建一个闭包,包含一个 if-else 语句来判断数组里是否含有 “A”。如果有,返回true
。
- // A closure for returnBool
- let mumHappy: (String) -> Bool = { grade in return grade == "A" }
如果你想让他更短,
- let mamHappy: (String) -> Bool = { $0 == "A" }
-
- mamHappy("A") // return true
- mamHappy("B") // return false
如果你对上面的两个例子感到困惑,那你还适应不了这个副本。你需要锻炼一下然后再回来。你可以重读我关于闭包的文章。链接
由于还没完成我们 stringFilter
函数的实现,让我们从离开的位置继续。
- func stringFilter (grade: [String], returnBool: (String) -> Bool)-> [String] {
-
- var happyGrade: [String] = []
- for letter in grade {
- if returnBool(letter) {
- happyGrade.append(letter)
- }
-
- }
- return happyGrade
- }
你的表情一定是 。我想说把刀放下,听我解释。通过 stringFilter
函数,你可以传递mamHappy
作为 returnBool
。然后调用 returnBool(letter)
,把每个项传递个 mamHappy
,最终就是 mamHappy(letter)
。
它返回 true
或者 false
。如果返回真,把 letter
加到只有 “A” 的 happyGrade
里。? 这就是为什么我妈妈在过去 12 年里感到开心的原因。
不管怎样,最终运行一下函数。
- let myGrade = ["A", "A", "A", "A", "B", "D"]
-
- let lovelyGrade = stringFilter(grade: myGrade, returnBool: **mamHappy**)
其实你不需要创建一个分离的 mamHappy
。可以直接在 returnBool
传递。
- stringFilter(grade: myGrade, returnBool: { grade in
- return grade == "A" })
我想让它更简洁。
stringFilter(grade: myGrade, returnBool: { $0 == “A” })
祝贺,如果你已经到了这里,那你已经做到了。我很感谢你的关注。现在让我们创建一个野蛮点的,广为人知的通用过滤器,你可以创建一堆你想要过滤的。比如,过滤你不喜欢的单词,过滤数组里大于 60 小于 100 的数。过滤只包含真值的布尔类型。
最棒的是它用一句话就可以形容。我们拯救了生命和时间。爱它,我们可以努力工作,但是我们要聪明的努力工作。
如果你对泛型代码感到不适,那你现在所在的位置并不正确,这里车速很快,赶快到安全的地方,名字是“Bob,泛型是什么鬼?”,然后带点武器回来继续。
我要创建一个含有 Bob
泛型的函数,你可以使用 T
或者 U
。但是你要知道,这是我的文章。
- func myFilter<Bob>(array: [Bob], logic: (Bob) -> Bool) -> [Bob] {
- var result: [Bob] = []
- for element in array {
- if logic(element) {
- result.append(element)
- }
- }
- return result
- }
让我们试着找点聪明的学生
- let AStudent = myFilter(array: Array(1...100), logic: { $0 >= 93 && $0 <= 100 })
-
- print(AStudent) // [93, 94, 95, ... 100]
- let answer = [true, false, true, false, false, false, false]
-
- let trueAnswer = myFilter(array: answer, logic: { $0 == true })
-
- // Trailing Closure
- let falseAnswer = myFilter(array: answer) { $0 == false }
幸运的是,我们不需要创建 myFilter
。Swift 已经为我们提供了一个默认的。现在我们创建一个从一到一百的数组,然后只要小于 51 的偶数。
- let zeroToHund = Array(1…100)
- zeroToHund.filter{ $0 % 2 == 0 }.filter { $0 <= 50 })
- // [2, 4, 6, 8, 10, 12, 14, ..., 50]
这就 OK 了。源码
我敢肯定你现在已经在想,怎么在你的应用和程序里使用函数式编程。记住,你使用什么语言都无所谓。
你需要清晰的是如何将函数式范式引用到更多的领域。在你 Google 之前,我建议你花一点时间消耗一两个脑细胞想一想。
从你理解 “filter” 背后的真正含义后,你现在可以更简单的 google 然后查看什么是 map
和reduce
,以及其他函数是怎么组成的。我希望能你在不冷不热的环境中学会游泳。
你现在只被你的想象力所限制。保持思考并 Google。
在我个人看来,这篇文章是黄金。它出现在我被闭包和函数式的东西弄得一脸懵逼的时候。人们都喜欢特别简单的原则。如果你喜欢我的解释,请分享并推荐给更多的人。我收到的心越多,我就会越像抽水泵一样,为每个人献出更伟大的内容!而且,更多的心意味着基于 Medium 算法上的更多观点。
有 Instagram 上的 geek 吗?我会发布我的一些日常并更新。欢迎大家随时添加我,跟我打招呼!@bobthedev
我的一个葡萄牙朋友 João 正在葡萄牙阿威罗组织一个 Swift 会议。不像许多人在的那里,这次会议的目的是实验性参与。观众与演讲者可以相互交流 - 带上你的笔记本电脑。这是我第一次的 Swift 会议。我超兴奋!除此之外,它也是经济实惠的。活动会在 2017 年的六月一号到二号举行。如果你有兴趣了解更多信息,请随时查看网站这里或下面的 Twitter。
SwiftAveiro (@SwiftAveiro) | Twitter
我在我的 Facebook 页面上给出详细的更新信息。一般在美国东部时间的上午八点,我会发表文章。2017 年,我立志成长为 Medium 上 iOS Geek 社区中第一的 iOS 博客。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。