函数式编程(Functional Programming)是相对于我们常用的面向对象和面向过程编程的另外一种开发思维方式,它更加强调以函数为中心。善用函数式编程思路,可以对我们的开发工作有很大的帮助和启发,今天我们就来讨论一下吧。

什么是函数式编程

我们用一个简单的例子为大家说明什么是函数式编程。 比如我们有这样一个结构:

struct Staff {

	var firstname: String
var lastname: String
var age: Int
var salary: Float }

Staff 结构定义了员工的基本信息,比如姓名,年龄,薪水等等。 我们再声明一个数组,里面存放我们的员工信息:

let staffs = [
Staff(firstname:"Ming", lastname:"Zhang", age: 24, salary: 12000.0),
Staff(firstname:"Yong", lastname:"Zhang", age: 29, salary: 17000.0),
Staff(firstname:"TianCi", lastname:"Wang", age: 44, salary: 30000.0),
Staff(firstname:"Mingyu", lastname:"Hu", age: 30, salary: 15000.0),
Staff(firstname:"TianYun", lastname:"Zhang", age: 25, salary: 12000.0),
Staff(firstname:"Wang", lastname:"Meng", age: 24, salary: 14000.0)
]

行为式思维

现在,我们需要找到所有姓张的员工信息(lastname 等于 Zhang),如果用我们惯用的开发思路,可以这样解决:

var staffOfZhang = [Staff]()
for staff in staffs { if staff.lastname == "Zhang" { staffOfZhang.append(staff) } } print(staffOfZhang)

这段代码首先声明了一个数组 staffOfZhang 用于存放符合条件的 Staff 实例,然后我们开始遍历我们的 staffs 数组,对于其中 lastname 等于 "Zhang" 的实例,将他们添加到 staffOfZhang 这个数组中。 这就完成了我们这个查找需求。

这种开发思路我们可以称作行为式思路。它侧重于告诉程序如何解决问题。比如我们定义了查询结果存放在哪里,以及如何遍历每一个实例,然后将符合条件的实例读取出来。

声明式思维

解决问题肯定不止一种方法,我们还可以换一种思维方式来解决这个问题,这种思维也可以称为声明式思维。我们可以使用 Arrayfilter 方法:

let staffOfZhang = staffs.filter { staff in
return staff.lastname == "Zhang"
}

这就是声明式思维的一个例子,这里 staffs.filter 函数接受一个闭包类型的参数,filter 方法会对 staffs 中的每一个元素都用传入 filter 的闭包调用一遍,根据这个闭包的返回值决定是否将这个元素作为符合条件(闭包的返回值如果为 true 则表示符合条件)的元素加入我们的查找结果中。

简单来说我们只需要在传入的闭包中声明好查找的规则,也就是 return staff.lastname == "Zhang" 这个表达式。这样我们就完成整个查找操作的处理了。

我们这里并没有告诉程序应该怎么去查找满足条件的元素的方法,而只是声明了一个规则。 这样做最大的好处就是能够减少我们的代码量,让我们的代码看起来非常的简洁,而且易理解。 这种方式就是函数式编程的一个例子。

First-Class function

谈到函数式编程就会提到 First Class Function。 这是个什么鬼呢~

简单来说 First Class Function 这样的函数不但可以进行简单的调用,还可以赋值给变量,也可以作为参数传递给另外一个函数,还可以作为函数的返回值。关于更详细的描述,可以参看 Wikipedia 上面的这篇文章:https://en.wikipedia.org/wiki/First-class_function

Swift 中的闭包就属于 First Class, 所以我们可以将闭包赋值给变量,传递给函数,作为返回值等等。对于我们上面的那个例子来说,我们就可以这样改写:

func filterZhang(staff:Staff) -> Bool {
return staff.lastname == "Zhang"
}
staffOfZhang = staffs.filter(filterZhang)

这正好和咱们刚才说的 First Class 对应上了。首先定义了一个 filterZhang 函数,接着将这个函数作为参数传递给 filter。

就这么简单,这也是 Swift 函数式编程的一个体现。

curry

那么,聪明的各位可能又想了,虽然我们可以定义这样一个函数,然后作为参数传递给 filter,但又有什么好处呢? 代码量并不比之前的少。

没错,这个问题问到关键之处了。且听继续分解。函数式编程的另一大特性就是 curry。这也是函数式编程的一个核心概念。

说白了 curry 就是用函数生成另一个函数。关于 curry 的详细探讨,还可以参考我之前的几篇文章:

神奇的 Currying

Swift 中 curry 特性的高级应用

下面咱们就用最简单的例子说明 curry 特性。我们可以将刚才定义的 filterZhang 方法改写一下:

func filterGenerator(lastnameCondition: String) -> (Staff) -> (Bool) {

    return {staff in
return staff.lastname == lastnameCondition
} }

那么我们来看一下 filterGenerator 的声明,首先它接收一个 String 类型的参数,然后它会返回另一个函数,这个函数接受一个 Staff 类型的参数,并且返回一个布尔值。

详细各位也都看出来了,我们定义的这个新函数 filterGenerator,其实就是对我们之前定义的 filterZhang 函数做了一个更高层的抽象。filterZhang 会过滤处所有 lastname 等于 Zhang 的员工实例。而使用 filterGenerator 可以生成任意条件的过滤函数:

let filterWang = filterGenerator("Wang")
let filterHu = filterGenerator("Hu")

大家看到了吧,我们对 filterGenerator 传入不同的参数,它就会根据这个参数生成一个新的过滤函数。然后我们就可以直接使用:

staffs.filter(filterHu)

这个调用会查询出所有 lastname 等于 Hu 的员工。这就是 curry 特性的精髓。比如,你在开发一个照片处理 APP,会对照片应用很多滤镜,并且这些滤镜还可以叠加,那么使用 curry 方式就可以让你的开发的效率和代码的健壮性提高很多。

更多应用

我们再来多看一些例子。假如我们现在想把所有员工的名字保存到另外一个数组中:

var names = [String]();

for staff in staffs {

    names.append("\(staff.lastname) \(staff.firstname)")

}

print(names)

再来看看我们使用函数式方式如何完成:

names = staffs.map{ staff in
return "\(staff.lastname) \(staff.firstname)"
}
print(names)

这次我们使用的是 map 方法,它会对数组中所有的元素依照我们指定的规则进行变换,然后生成一个新的数组。这样我们只需要在闭包中声明变换规则就完成了。又是声明式思维的一种体现。

再比如,我们想计算出所有员工的平均工资:

var totalSalary = Float(0.0)

for staff in staffs {

    totalSalary += staff.salary

}
print(totalSalary / Float(staffs.count))

我们再看看如何用函数式的思路来完成:

let averageSalary = staffs.reduce(0) { total, staff in
return total + staff.salary / Float(staffs.count)
}
print(averageSalary)

这次我们使用 Array 的 reduce 函数,这个函数接受两个参数,第一个参数是初始值,然后 reduce 会依次让每一个元素和这个值进行操作,然后将计算结果传递给下一个元素的调用。

对于我们这里,就是依次计算每个员工对于平均工资的基数,然后将他们相加到一起就是整体的平均工资了。我们这里依然只声明了一个规则 return total + staff.salary / Float(staffs.count)。这样代码读起来非常的清晰,很明确的说明了我们要干什么。

结语

到这里,函数式编程的基本思路就都给大家介绍完了。总之呢函数式编程的主要特性就是声明式思维以及 curry 传递思维。它能够让我们用很优雅的语法实现在以前看来比较繁杂的逻辑,这也是它的最大优势。并且它还衍生除了一些分支,比如响应式编程,非常流行的 ReactiveCocoa 库正式 Cocoa 平台对应响应式编程的实现。这些新的编程方式希望用一种更加优雅简洁的方式来解决我们开发中的问题。

最通俗易懂的方式让你理解 Swift 的函数式编程的更多相关文章

  1. swift 之函数式编程(一)

    1. 什么是函数式编程? 函数式编程是阿隆佐思想的在现实世界中的实现, 它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及异变物件. 函数式编程的最重要基础是λ演算.而且λ演算的函數可以接受函 ...

  2. swift之函数式编程

    函数式编程初探 最近初学swift,和OC比,发现语言更现代,也有了更多的特性.如何写好swift代码,也许,熟练使用新特性写出更优秀的代码,就是答案.今天先从大的方向谈谈swift中的编程范式-函数 ...

  3. 理解iOS与函数式编程

    有时候,一个关键字就是一扇通往新世界的大门.两年前,身边开始有人讨论函数式编程,拿关键字Functional Programming一搜,全是新鲜的概念和知识,顺藤摸瓜,看到的技术文章和框架也越来越多 ...

  4. swift之函数式编程(四)

    文章内容来自<Functional Programing in Swift>,具体内容请到书中查阅 Map, Filter, Reduce Functions that take func ...

  5. swift之函数式编程(二)

    本文的主要内容来自<Functional Programming in Swift>这本书,有点所谓的观后总结 在本书的Introduction章中: we will try to foc ...

  6. swift之函数式编程(五)

    文章内容来源于<Functional Programing in Swift>,详情请看原著 The Value of Immutability swift 对于控制值改变有一些机制.在这 ...

  7. swift之函数式编程(三)

    文章来源于<Functional Programing in Swift>,本系列仅仅是观后概括的一些内容 Wrapping Core Image 上一篇文章我们介绍了 高阶函数并且展示了 ...

  8. 深入理解 Swift 派发机制

    原文: Method Dispatch in Swift作者: Brain King译者: kemchenj 译者注: 之前看了很多关于 Swift 派发机制的内容, 但感觉没有一篇能够彻底讲清楚这件 ...

  9. 理解Swift中map 和 flatMap对集合的作用

    map和flatMap是函数式编程中常见的概念,python等语言中都有.借助于 map和flapMap 函数可以非常轻易地将数组转换成另外一个新数组. map函数可以被数组调用,它接受一个闭包作为參 ...

随机推荐

  1. 【2005】N只猴子选大王

    Time Limit: 3 second Memory Limit: 2 MB N只猴子选大王.选举办法如下:从头到尾1.2.3报数,凡报3的退出,余下的从尾到头1.2.3报数,凡报3退出:余下的又从 ...

  2. Windows7-32位系统下R语言链接mySQL数据库步骤

    安装R和MySQL在此就不再多说了.网上有非常多教程能够找到.以下直接进入到odbc的安装流程. 1.下载安装mysql-connector-odbc-5.x.x-win32.msi 下载地址:htt ...

  3. Java并发包探秘 (一) ConcurrentLinkedQueue

    本文是Java并发包探秘的第一篇,旨在介绍一下Java并发容器中用的一些思路和技巧,帮助大家更好的理解Java并发容器,让我们更好的使用并发容器打造更高效的程序.本人能力有限,错误难免.希望及时指出. ...

  4. arm交叉编译Valgrind

    1. wget http://valgrind.org/downloads/valgrind-3.9.0.tar.bz2 tar xvf valgrind-3.9.0.tar.bz2 cd valgr ...

  5. winscp ppk无需密码登录(失败)

    http://blog.csdn.net/catoop/article/details/8284803 按上文将Linux下生成的密钥文件id_rsa通过puttygen生成对应的.ppk文件,用wi ...

  6. ios开发处理服务器返回的时间字符串

    #import <Foundation/Foundation.h> void other(); void string2date(); int main(int argc, const c ...

  7. 2016.3.14__CSS 定位__第六天

    假设您认为这篇文章还不错.能够去H5专题介绍中查看很多其它相关文章. CSS 定位机制 CSS中一共同拥有三种基本定位机制:普通流.浮动.绝对定位. 假设不进行专门指定.全部的标签都在普通流中定位. ...

  8. 排序算法 基于Javascript

    写在前面 个人感觉:javascript对类似排序查找这样的功能已经有了很好的封装,以致于当我们想对数组排序的时候只需要调用arr.sort()方法,而查找数组元素也只需要调用indexOf()方法或 ...

  9. springMVC中前台ajax传json数据后台controller接受对象为null

    在jquery的ajax中,如果没加contentType:"application/json",那么data就应该对应的是json对象,反之,如果加了contentType:&q ...

  10. 【t061】游览路线

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 话说LCINF信息组来到烟台参加夏令营.一天,大家提议出去游玩,来到了烟台最繁华的地方.由于他们对烟台 ...