前言
最近花了不少时间接触学习javascript的函数式的编程方式,而后为了加深理解,又去折腾haskell。
不同于人们比较熟悉的命令式编程,如面向对象编程(oop),函数式编程(fp)是一种更加抽象,更加‘数学’的编程方式。
当然,也是一种更加‘痛苦’的编程方式,尤其是刚接触时,总是难以摆脱的命令式的思维方式,大脑回路总是会迷路。
不过幸运的是,javascript天生具备了函数式编程的基本元素,所以学习的起点不会太低。
初接触
第一个实例,函数式编程是如何做一个番茄炒鸡蛋的。
伪代码,对比oop:
Class Chef{
cook(m1,m2){
return m1+m2
}
}
chef = new Chef
food = chef.cook('番茄','鸡蛋')
food //番茄炒蛋。
fp的方式
getMaterial(m){
return function(){
return m
}
}
cook(getM1,getM2){
return getM1()+getM2()
}
food = cook(getMaterial('番茄'),getMaterial('鸡蛋'));
food //番茄炒蛋
那么现在,站在内存(拟作:你)变化的角度上,这两者的区别之处。
oop
1.你面前出现了一个厨师
2.你前面出现了番茄,鸡蛋
3.厨师把番茄和鸡蛋炒在一起
4.厨师把番茄炒蛋装在盘子上
4.你获得了番茄炒蛋
fp
1.你拿一个空盘子,你决定弄点东西在上面
2.你面前出现了一个传说中的厨具。 //没错!小当家同款厨具,只要丢材料进去,它就能弄出美味佳肴!
3.你虽然没有材料,但幸运的是,你有两张藏宝图,分别标示了番茄和鸡蛋的位置。通过藏宝图,就能找到所需的材料。
4.你把两张藏宝图扔到厨具里,告诉它,它得自己去找材料。 //传说中的厨具就是这么牛逼!
5.厨具获得了番茄
6.厨具获得了鸡蛋
7.厨具终于炒了番茄和鸡蛋
7.finally,你获得了番茄炒蛋
对比一下这个过程,可以发现:
oop方式总是是在告诉系统,第一步应该干什么(搞个厨师)然后干什么(弄到番茄和鸡蛋)、按部就班,最后你就能得到想要的值(番茄炒蛋)。
fp方式呢,恰恰相反,它是惰性的。只有你需要什么的时候,函数才会运算,才会返回数值,而不是一开始就存在的。
就好像学渣考90分,是因为在考试之前,他努力学习,到了90分。
这是结果。
而学霸考90分,只是因为考试的时候,做到90分时,懒癌发作,不想做题了。
这是过程。
fp的特性
这里列举了当前接触到fp中编程思想中的几个重要特性
1.不可变数据
2.函数是一等公民,即能作为参数,也可以是返回值
3.惰性求值
1.不可变数据
由于fp中都是函数,为了保证程序的可靠性,同样的参数,传入同一套的函数中,必须保证结果也是一样的。如:
let o = {name:'zhouyg'};
r1 = fn1(fn2(fn3(o)))
r2 = fn1(fn2(fn3(o)))
r1 === r2 //true
在javascript中的由于Array和Object的类型都是引用传递的。如果在函数内部改变了改变了原始o的值,那么改变了原始o的值,那么必然导致r1和r2的结果不一样。导致程序不可靠,不可维护。
这是javascript的特性引起的,需要额外的手段补救。
每次传递Object和Array时候,都做一个拷贝,使用拷贝后的对象作为函数参数
或者使用某些数据结构工具,例如F家著名的immutable.js
2.函数是一等公民
javascript天然满足,常见的各种回调。
3.惰性求值
顾名思义,只有在需要用到的才去计算。这里强行设定一种情景,如一个加法函数:
没有惰性求值
function add(n1,n2){
if(n1<5){
return n1
}else{
return n1+n2
}
}
result = add(add(1,2),add(3,4)) //相当于add(3,4)的计算是浪费的。
result//3
惰性求值
function add(n1,n2){
return n1+n2;
}
function preAdd(n1,n2){
return function(){
return add(n1,n2)
}
}
function doAdd(fn1,fn2){
n = fn1()
if(n<5){
return n //只需要运行fn1,得到一个计算结果即可。
}else{
return add(fn1,fn2())
}
}
result = doAdd(preAdd(1,2),preAdd(3,4))
result//10
对比一下可知,在javascript中的惰性求值,相当于先把参数先缓存着,return一个真正执行的计算的函数,等到需要结果采去执行。
这样的好处在于比较节省计算,尤其有时候这个在函数是不一定需要这个参数的时候。
最后
这里只是简单的回溯最近学习的fp的相关内容。并不是为了比较fp方式和其它编程方式的优劣,而是希望能够站在另外的一个角度,活跃大脑的思维,开拓视野,以更丰富的姿势来解决问题,毕竟俗话说得好,姿势就是力量。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。