函数式编程的杂七杂八(非常乱)
1.把大象关进冰箱里问题
面向过程:
把大象关进冰箱里:
把冰箱门打开=>
大象放冰箱里=>
冰箱门关上
面向对象:
冰箱.开门()
冰箱.放入(大象)
冰箱.关门()
函数式:
关进(冰箱,大象):
关门(放入(开门(冰箱),大象))
——————————————————————————
2.
C#引入FP的特性被某些人称作语法糖,我一直有意把这种观点拖出来痛打一顿(注意不是把持有这种观点的人拖出来痛打),至于持有这种观点的人,我禁止你们写a+b*c,以后给我统统去写汇编
MOV EAX, [b]
MUL EAX, [c]
ADD EAX, [a]
当然前提是我能禁止得了。
——————————————————————————
3.语言特性和编程范式完全是两码事
从某种程度上来说,C语言既能进行函数式编程,又能进行
在支持面向对象的语言里,我完全可以设计一个类似下面的类
class Search {
virtual List Extend(Node node)
virtual bool Beam(Node node)
virtual bool CheckFinish(Node node)
Node Search()
{
OrderedCollection openlist=new OrderedCollection();
openlist.put(node);
while(!openlist.empty()){
Node current=openlist.get();
if(!Beam(current)){
List extended=Extend(current);
for(var i=0;i<extended.length;i++) {
if(Finish(extended[i]))
return extended[i];
openlist.put(extended[i]);
}
}
}
return null;
}
}
我甚至用到了一个设计模式:模板方法。
不过这个代码跟面向对象没有半点关系,我只是在利用某些语言特性而已。
对于任何一门编程语言而言 把class当成"类"这个意思都是程序员一厢情愿的想法——编译器才不在乎这个关键字是class还是glass,只是个关键字而已。
父亲们在这几这些语言的时候,当然是有意识地提醒你这个特性将被用于面向对象,但人家没有强迫你这样做。
于是某些不安分的同学(比如我),就喜欢拿面向对象特性来干一些不是面向对象的事,或者拿一些不是面向对象的特性来干面向对象的事情。
所以对于C而言 closure无非是比函数指针多了一个void*而已,多传一个参数又不会少块肉——你敢说这不是语法糖?
不过拿这样的方式来做FP确实闻所未闻,这从一个侧面说明了语言特性对于人的思维影响还是很大的。
所以尽量不要做那些扭着来语言特性来的事情,比如,用人家叫Exception的东西表达正常逻辑。
——————————————————————————
4.给10个按钮改变颜色
for(var i = 0 ; i < 10 ; i++ )
button[i].background = XXXX;
这段简单的代码丢失了一个重要信息,那就是"给10个按钮改变颜色的顺序是不要求的"。
所以想这段代码翻译回去变成原来的语义是不能的。
但是函数式可以。
任何一个函数都不介意自己的参数被求值的顺序。
但是任何一个函数不会在自己参数被求值之前执行。
所以 放入(开门(冰箱),大象) 一定会在 开门(冰箱) 之后执行。
这些被丢掉的信息,在多核处理器中尤其重要。
你写
dotA();
dotB();
的时候,我肯定不知道doA和doB是否能并行执行。
但是
do(dotA(),dotB())
这时候,我肯定知道doA和doB可以同时做。
神啊 原谅我用dota做SEO吧......
——————————————————————————
5.范式是认识世界的方法论
使持续工作12小时(皮鞭抽(程序员),工作用计算机) ,
对于完成一个项目,有些老板是这样理解的;
皮鞭抽程序员=>勒令程序员用计算机工作=>等待完成
也有些老板是这样理解的。
当老板思考"如何完成工作"的时候,他们也许会使用完全不同的方法得到相同的结论。
这其实与编程无关,当你开始思考这世界的时候,一定会选择其一来理解世界。
函数式不算阳春白雪,其实你一直在用了。
世界上的是,无论你想做什么,无非是把一些东西变成另一些东西,这就是函数式编程的世界观。
只是你不习惯把这种思维用到程序中而已。
你总是想告诉计算机,先这样,再这样,然后那样。
所以如果你真正希望理解函数式,关键是观念的转变——你必须完全抛弃先后的想法。