陈两岁|函数式编程
来源:酷壳网-陈皓链接:
https://coolshell.cn/articles/10822.html
当我们说起函数式编程来说 , 我们会看到如下函数式编程的长相:
函数式编程的三大特性:
immutabledata不可变数据:像Clojure一样 , 默认上变量是不可变的 , 如果你要改变变量 , 你需要把变量copy出去修改 。 这样一来 , 可以让你的程序少很多Bug 。 因为 , 程序中的状态不好维护 , 在并发的时候更不好维护 。 (你可以试想一下如果你的程序有个复杂的状态 , 当以后别人改你代码的时候 , 是很容易出bug的 , 在并行中这样的问题就更多了)
firstclassfunctions:这个技术可以让你的函数就像变量一样来使用 。 也就是说 , 你的函数可以像变量一样被创建 , 修改 , 并当成变量一样传递 , 返回或是在函数中嵌套函数 。 这个有点像Javascript的Prototype(参看Javascript的面向对象编程)
尾递归优化:我们知道递归的害处 , 那就是如果递归很深的话 , stack受不了 , 并会导致性能大幅度下降 。 所以 , 我们使用尾递归优化技术——每次递归时都会重用stack , 这样一来能够提升性能 , 当然 , 这需要语言或编译器的支持 。 Python就不支持 。
函数式编程的几个技术
map&reduce:这个技术不用多说了 , 函数式编程最常见的技术就是对一个集合做Map和Reduce操作 。 这比起过程式的语言来说 , 在代码上要更容易阅读 。 (传统过程式的语言需要使用for/while循环 , 然后在各种变量中把数据倒过来倒过去的)这个很像C++中的STL中的foreach , find_if , count_if之流的函数的玩法 。
pipeline:这个技术的意思是 , 把函数实例成一个一个的action , 然后 , 把一组action放到一个数组或是列表中 , 然后把数据传给这个actionlist , 数据就像一个pipeline一样顺序地被各个函数所操作 , 最终得到我们想要的结果 。
recursing递归:递归最大的好处就简化代码 , 他可以把一个复杂的问题用很简单的代码描述出来 。 注意:递归的精髓是描述问题 , 而这正是函数式编程的精髓 。
currying:把一个函数的多个参数分解成多个函数 , 然后把函数多层封装起来 , 每层函数都返回一个函数去接收下一个参数这样 , 可以简化函数的多个参数 。 在C++中 , 这个很像STL中的bind_1st或是bind2nd 。
higherorderfunction高阶函数:所谓高阶函数就是函数当参数 , 把传入的函数做一个封装 , 然后返回这个封装函数 。 现象上就是函数传进传出 , 就像面向对象对象满天飞一样 。
还有函数式的一些好处
parallelization并行:所谓并行的意思就是在并行环境下 , 各个线程之间不需要同步或互斥 。
lazyevaluation惰性求值:这个需要编译器的支持 。 表达式不在它被绑定到变量之后就立即求值 , 而是在该值被取用的时候求值 , 也就是说 , 语句如x:=expression;(把一个表达式的结果赋值给一个变量)明显的调用这个表达式被计算并把结果放置到x中 , 但是先不管实际在x中的是什么 , 直到通过后面的表达式中到x的引用而有了对它的值的需求的时候 , 而后面表达式自身的求值也可以被延迟 , 最终为了生成让外界看到的某个符号而计算这个快速增长的依赖树 。
determinism确定性:所谓确定性的意思就是像数学那样f(x)=y , 这个函数无论在什么场景下 , 都会得到同样的结果 , 这个我们称之为函数的确定性 。 而不是像程序中的很多函数那样 , 同一个参数 , 却会在不同的场景下计算出不同的结果 。 所谓不同的场景的意思就是我们的函数会根据一些运行中的状态信息的不同而发生变化 。
上面的那些东西太抽象了 , 还是让我们来循序渐近地看一些例子吧 。
我们先用一个最简单的例子来说明一下什么是函数式编程 。
先看一个非函数式的例子: