概述
函数式编程是一种编程模型
将计算机运算看做是数学中函数的计算,并且避免了状态以及变量的概念。函数式编程的特点是,把控制权让渡给语言。
命令式编程天生的缺陷却使并行编程模型变得非常复杂,无论是信号量,还是锁的概念,都使程序员不堪其重。函数式编程就这样被推到了前线。状态不可变解决了并发编程中的最大问题
函数是函数式编程的第一型。
在面向对象编程中,我们把对象传来传去,那在函数式编程中,我们要做的是把函数传来传去,而这个,说成术语,我们把他叫做高阶函数。
在函数式编程中,函数是基本单位,是第一型,他几乎被用作一切,包括最简单的计算,甚至连变量都被计算所取代。在函数式编程中,变量只是一个名称,而不是一个存储单元,这是函数式编程与传统的命令式编程最典型的不同之处。
函数式编程关注做了什么,不关注怎么做
//把集合中的每个数字加一
list.map(x => (x+1))
做什么?每个元素加一
怎么做?不关心
特性
不可变性
不可变性指的是变量一旦确定,就不能再修改,这样做的好处是极大地方便了并发编程,变量的不变性,解决多线程/并发时锁的问题。大幅度提升多核处理器的性能。其次是降低了bug发生的概率。bug的发生经常出现在变量的位置。
惰性计算
惰性求值,有利于系统优化函数式程序的执行。将表达式复制给变量的时候不计算表达式的值,只在变量第一次被使用的时候才进行计算。惰性求值能大幅提高并行计算的性能。
- 允许我们创建无线长度的序列
- 减少存储空间
- 产生更高效的代码
//演示惰性求值
list = [1,2/0,12]
print list.length
//输出是3。如果不是惰性求值会报错
闭包和高阶函数
柯里化
和命令式编程的对比
- fp关注数据的映射,cp关心解决问题的步骤
- fp注重读关系的描述,任何能在计算机中的东西之间的对应关系都可以描述
- cp是一个指令序列,如何取数据,变量,控制,循环等。fp是一个表达式
- 命令式编程是面向对象的,自顶向下。函数式编程是自底向上。通过不断地函数组合达到目的
也就是先定义最基本的操作,然后不断组合,不断堆积以满足你的所有需要,如sql定义了select, from, where…这几个组合子,来满足你的查询需求,同理函数式语言会提供foreach, map等组合子(操作)来满足你的需求,所以你必须自下而上的设计你的代码结构,并且满足你的需求,当你只用组合子写代码时,你会发现你写的全是高阶逻辑 - 程序的状态:
- 命令式编程通过变量的变化表明状态、
- 函数式编程通过函数创建新的函数或者返回值来保存程序的状态,每个函数的返回值,参数都带便了程序的状态
思维
- 数据不可变
- 一切皆表达式
- 函数是第一公民
- 递归替代循环
- 高阶函数,lamba演算
- 支持高并发
fp的本质
- 高阶编程
- 没有副作用
- 函数的所有功能都是为了一个返回值,不做任何其他的事情。
- 不修改变量的值
- 使得fp的各部分的执行顺序可以随意打乱,有利于高并发。惰性求值,无需加锁
构造单元
Map
对集合中的每个元素进行映射,结果返回一个新的集合。
seq.map(s => fun(s))
seq.map(s => s+100)
seq.map(_+100)
Reduce
- 用一个累积量来搜集集合元素。每次运算两个,运算的结果当作第一个,再从集合中选取下一个进行运算,直到集合为空。
- 使用reduce时,需要规范化。直接使用reduce,方向是不被保证的。通过使用reduceLeft和reduceRight我们可以强制处理元素的方向。
- 和reudce类似的是fold,对比如下:
reduce | fold |
---|---|
可以没有 | 必须设定一个初始值 |
返回类型必须和列表的返回类型相同 | 没有限制 |
seq.reduce(s,fun(s))
seq.reduceLeft(_+_)
seq.reduceLeft((x:Int,y:Int) => func(x,y))
seq.reduceRight((x:Int,y:Int) => func(x,y))
Filter
根据用户的定义将符合条件的值筛选出来,形成新的集合。
scala中提供了多种筛选方法。
seq.filter(s,fun(s))
seq.filter((x:Int) => 条件)
seq.filter((x:Int) => x>n)
// 选择第一个符合条件的
seq.find(_fun())
// 集合分片
seq.partion(条件)
递归迭代
迭代一定能转化成递归,递归不一定能转换成迭代。
递归中一定有迭代,迭代中不一定有递
递归是函数自身内部调用自己 ,问题的规模会越来越小。类似树形结构
- 有出入口
- 递推阶段:缩减规模,把问题分解
- 回归阶段:获得最简单的情况之后逐步返回
迭代是运算结果作为输入,问题规模基本不变。类似环形结构