吾本轻狂|函数式编程

来源:酷壳网-陈皓 链接:
当我们说起函数式编程来说 , 我们会看到如下函数式编程的长相:

  • 函数式编程的三大特性:
    • immutable data 不可变数据:像Clojure一样 , 默认上变量是不可变的 , 如果你要改变变量 , 你需要把变量copy出去修改 。 这样一来 , 可以让你的程序少很多Bug 。 因为 , 程序中的状态不好维护 , 在并发的时候更不好维护 。 (你可以试想一下如果你的程序有个复杂的状态 , 当以后别人改你代码的时候 , 是很容易出bug的 , 在并行中这样的问题就更多了)
    • first class functions:这个技术可以让你的函数就像变量一样来使用 。 也就是说 , 你的函数可以像变量一样被创建 , 修改 , 并当成变量一样传递 , 返回或是在函数中嵌套函数 。 这个有点像Javascript的Prototype(参看Javascript的面向对象编程)
    • 尾递归优化:我们知道递归的害处 , 那就是如果递归很深的话 , stack受不了 , 并会导致性能大幅度下降 。 所以 , 我们使用尾递归优化技术——每次递归时都会重用stack , 这样一来能够提升性能 , 当然 , 这需要语言或编译器的支持 。 Python就不支持 。
  • 函数式编程的几个技术
    • map (把一个表达式的结果赋值给一个变量)明显的调用这个表达式被计算并把结果放置到x中 , 但是先不管实际在x中的是什么 , 直到通过后面的表达式中到x的引用而有了对它的值的需求的时候 , 而后面表达式自身的求值也可以被延迟 , 最终为了生成让外界看到的某个符号而计算这个快速增长的依赖树 。
    • determinism 确定性:所谓确定性的意思就是像数学那样 f(x) = y, 这个函数无论在什么场景下 , 都会得到同样的结果 , 这个我们称之为函数的确定性 。 而不是像程序中的很多函数那样 , 同一个参数 , 却会在不同的场景下计算出不同的结果 。 所谓不同的场景的意思就是我们的函数会根据一些运行中的状态信息的不同而发生变化 。
上面的那些东西太抽象了 , 还是让我们来循序渐近地看一些例子吧 。
我们先用一个最简单的例子来说明一下什么是函数式编程 。
先看一个非函数式的例子:
int cnt;voidincrement{cnt++;}那么 , 函数式的应该怎么写呢?
int increment(int cnt){return cnt+1;}你可能会觉得这个例子太普通了 。 是的 , 这个例子就是函数式编程的准则:不依赖于外部的数据 , 而且也不改变外部数据的值 , 而是返回一个新的值给你 。
我们再来看一个简单例子:
def inc(x):def incx(y):return x+yreturn incxinc2 = inc(2)inc5 = inc(5)print inc2(5) # 输出 7print inc5(5) # 输出 10我们可以看到上面那个例子inc函数返回了另一个函数incx , 于是我们可以用inc函数来构造各种版本的inc函数 , 比如:inc2和inc5 。 这个技术其实就是上面所说的Currying技术 。 从这个技术上 , 你可能体会到函数式编程的理念:把函数当成变量来用 , 关注于描述问题而不是怎么实现 , 这样可以让代码更易读 。
Map int main {string s="hello";string out;transform(s.begin, s.end, back_inserter(out), ::toupper);cout << out << endl;// 输出:HELLO}在上面Python的那个例子中我们可以看到 , 我们写义了一个函数toUpper , 这个函数没有改变传进来的值 , 只是把传进来的值做个简单的操作 , 然后返回 。 然后 , 我们把其用在map函数中 , 就可以很清楚地描述出我们想要干什么 。 而不会去理解一个在循环中的怎么实现的代码 , 最终在读了很多循环的逻辑后才发现原来是这个或那个意思 。下面 , 我们看看描述实现方法的过程式编程是怎么玩的(看上去是不是不如函数式的清晰?):


推荐阅读