欢迎浏览我的博客 获取更多精彩文章
函数式编程的思考
什么是函数式编程?
为了要理解什么是函数式编程,我们首先要知道什么不是函数式编程.通常,我们用的最多的编程范式是命令式编程,它由"工作"组成,与计算机的执行方式相同,即运行一行命令,做一件事,然后运行下一行命令.这意味着,当我们使用命令行式编程的时候,抽象地来看,代码中会有一个初始状态,一个转换过程和一个终止状态.
比如,当我们要表示一个a+b的程序,我们会这样表示
if b equals 0 => return a
else => return a plus b
而函数式编程,是由"是什么"的元素来构成的,比如,对于命令式编程,2+3对他来说是执行一个2+3的命令,最后输出5,而对于函数式编程,2+3就是5
或许这样子说会有一点令人迷惑,但是我们会在以后慢慢地清晰这个概念
函数式编程的特点
函数式编程最大的特点就是,他是没有副作用的.具体来说:
- 他不会改变外部变量的值
- 不会将anything输出
- 不会抛出异常(并不表示不会出错)
换句话来说,FP是一种抽象的黑盒方法的表示.
同时,函数式编程其实是相对的,在一个函数的内部,他的实现可以是命令行式的,但是可以让外部用函数式来进行调用,并且无副作用,那么在外部看来,这个就是一个符合函数式编程的函数
其次,函数式编程是引用透明的,其中包括了
- 他是独立的,他不会依赖除了输入以外的外部条件来进行工作,他不会从文件,数据库或者网络中读取数据
- 他是确定的,每一次输入相同的参数,一定会返回相同的输出
- 他不会使外部的变量与状态发生改变
函数式编程的优点
- 易于推断.根据函数式的特性,我们可以确定输入和输出的对应关系,这使得我们不需要太多的测试用例来验证其正确性
- 易于测试
- 提高模块化的程度
- 使得代码重用更加简单
如何将命令行编程转化为函数式编程范式
在上面说了这么多的特点,那么,假如我们之前的代码都是用命令行范式来编写的,要如何将其部分模块代码转化为函数式编程呢?
抽象地来看,在命令行编程中,我们需要确定的是初始状态,转化过程和结束过程.在转化过程中,会获取除了初始状态以外的其他输入状态,那么,我们只要把所有输入状态独立出来,单独地将转化过程抽出来,似乎就可以实现函数式的编程了.我们来看一些实际的例子.
public class DonutShop{
public static Donut buyDonut(CreditCard c){
Donut d = new Donut();
c.charge(Donut.price) //1
return d;
}
}
在这里,我们模拟了一个刷信用卡购买物品的过程,在这个过程(1)中,我们发现信用卡付款时,不仅会改变信用卡的额度,他还牵涉到了从银行中查询,向银行发送消息等过程.那么我们要怎么改造它呢?
首先我们可以定义一个Payment类,里面存储信用卡信息和账户的额度信息
然后我们需要定义一个Purchase类,里面会存储需要购买的商品,以及付款手段(Payment类对象)
我们将购买这个行为定义成为一个函数
public Tuple<Donut,Payment> buy(CreditCard c){
Donut d = new Donut();
Payment p = new Payment(c,Donut,prize);
return new Tuple<d,p>;
}
这样,我们就把购买行为定义成了一个无副作用的方法了.
总结
- 函数式编程就是用函数来进行编程,返回结果,没有任何副作用
- 函数式编程容易推断,也容易测试
- 函数式编程提供了高层次的抽象和重用
- 函数式程序比命令式程序更加具有健壮性
- 函数式程序由于避免了共享可变的状态,因此,他在多线程环境中会更加安全.