纯函数与函数式编程初探
纯函数概念
满足一个条件:输入值确定,输出值就确定的函数
为什么需要存函数
非混乱的结果
例子1
一个计算扣除所得税后工资功能函数,假设直接扣除20%的比例:
priate static count(double money){
return money * 0.8;
}
上面是普通的函数,下面,我们考虑个人所得税计算税率随着时间变化的问题,如果2020.4.24前是扣除0.2,之后调整为扣除0.15,我们可以在方法加个时间判断:
priate static count(double money){
double rate = 0.8
if(today() > Date(2020.4.24)){
rate = 0.85;
}
return money * rate;
}
上面这个函数,就不是纯函数,它的输出结果不是完全由输入结果决定的
非纯函数带来的问题
上面的那个例子,大概可以看到,非纯函数带来的问题:输出结果并非完全由输入结果确定的,这会使得我们的测试变得很麻烦,因为不同的时间测试的结果可能不一样!我同样的输入参数,今天执行和明天执行,得到的结果不一样,那么,对于函数使用者而言,他回觉得这个函数有问题,毕竟他不知道函数内部实现是怎样的。
纯函数使得结果可控
所以,上面就是我们使用纯函数的理由之一:使得结果可控,使得程序的状态是确定的,程序就是需要确定性,同样的参数就应该有同样的结果,而不应该依赖外部的状态。
所以,上面的例子,我们需要吧时间也作为参数传进来,这样才能保证结果是可控的,状态是确定的:
priate static count(Date date,double money){
double rate = 0.8
if(date > Date(2020.4.24)){
rate = 0.85;
}
return money * rate;
}
函数式编程与纯函数
何为函数式编程
java,c++等语言,基本都是过程式的编程:立足于我怎么做?比如,有个业务场景,我们需要做的是读一批学生数据,找到里面性别是女的学生:
List<Student> students ;
List<Student> studentsFemal;
for(let s in students){
if("femal".equal(s.sex)){
studentsFemal.add(s);
}
}
上面代码没啥问题,但是我们想,我们日常编程,经常会有这种业务场景:在一堆元素中,查找某些特征的元素,我们能否把这个逻辑抽象出来,封装为一个纯函数呢?答案是肯定可以的(当然,这个用java来不太好搞也不直观,下面是js的代码块):
let list = [];
for(let i=0;i<10;i++){
list.push(i);
}
const findItemInList = list => howToFind =>{
let newList = [];
for(let item of list){
howToFind(item) && newList.push(item);
}
return newList
}
console.log(findItemInList(list)(item =>{
return item > 5
}));
就这?函数式编程?就找个元素搞得那么蛋疼?有必要?
当然,上面那么简单的例子的确是没啥必要,但是主要是想通过上面的例子传递一种思想:我们日常编程中,可以把一些重复的操作,通过函数式编程的思想方法抽象起来,封装成一个所谓的高阶纯函数,以便复用:
上面例子,我们看64行调用findItemInList方法时,是这样调用的:
findItemInList(list)(item =>{
return item > 5
})
findItemInList(list)执行完成后,返回一个函数,我们通常把这种返回结果是函数的函数,叫做所谓高阶函数,这个高阶函数,是一个通用的、专门用于查找符合某个条件的函数,它是一种逻辑的抽象,而具体的逻辑,我们通过传入一个函数来指定,即:
(item =>{
return item > 5
})
然后就可以得到我们的结果。
总的来说:findItemInList(list)本身抽象了遍历这个过程,而findItemInList(list)(item =>{return item > 5})则对这个过程进行了调用。在这过程当中,findItemInList(list)绑定了list这个列表(绑定了数据),findItemInList(list)(item =>{return item > 5})在绑定数据的基础上绑定了对数据的操作,这个思想其实和过程式编程有一个非常大的区别就是:
过程式编程我们找具有某个特征的一些元素的时候,我们是遍历这个列表进行查找,强调怎么做;而函数式编程则是表现出我要做什么:我要找元素(对应findItemInList(list)),这个元素是元素数组中值大于5的元素(对应findItemInList(list)(item =>{return item > 5}))。
纯函数与函数式编程的联系
一般而言,我们需要实现函数式编程,我们所封装的方法应该是抽象的,应该是和外部状态无关系的,也就需要是纯函数的,这样才能保证抽象的方法可复用而且输出结果只决定于输入参数。
其实,js里面本身就有封装好的高阶函数,在一定程度上体现了函数式编程的思想,比如数组的map,filter,reduce等方法。而jdk1.8之后lambada表达式也支撑了函数式编程。所以,总的来说,学习下函数式编程的思想,对于提高我们编程的能力还是很有帮助的,他让我们从另外一个角度去思考如何解决和抽象问题。
end总结
当然啦,函数式编程还有很多应用,我上面也只是总结了一些皮毛而已,如果想真的了解函数式编程,可以学习下HasKell等真正的函数式编程语言。