http://www.cnblogs.com/huxi/archive/2011/06/18/2084316.html

1. 函数式编程概述

1.1. 什么是函数式编程?

函数式编程使用一系列的函数解决问题。函数仅接受输入并产生输出,不包含任何能影响产生输出的内部状态。任何情况下,使用相同的参数调用函数始终能产生同样的结果。

在一个函数式的程序中,输入的数据“流过”一系列的函数,每一个函数根据它的输入产生输出。函数式风格避免编写有“边界效应”(side effects)的函数:修改内部状态,或者是其他无法反应在输出上的变化。完全没有边界效应的函数被称为“纯函数式的”(purely functional)。避免边界效应意味着不使用在程序运行时可变的数据结构,输出只依赖于输入。

可以认为函数式编程刚好站在了面向对象编程的对立面。对象通常包含内部状态(字段),和许多能修改这些状态的函数,程序则由不断修改状态构成;函数式编程则极力避免状态改动,并通过在函数间传递数据流进行工作。但这并不是说无法同时使用函数式编程和面向对象编程,事实上,复杂的系统一般会采用面向对象技术建模,但混合使用函数式风格还能让你额外享受函数式风格的优点。

1.2. 为什么使用函数式编程?

函数式的风格通常被认为有如下优点:

  • 逻辑可证 
    这是一个学术上的优点:没有边界效应使得更容易从逻辑上证明程序是正确的(而不是通过测试)。
  • 模块化 
    函数式编程推崇简单原则,一个函数只做一件事情,将大的功能拆分成尽可能小的模块。小的函数更易于阅读和检查错误。
  • 组件化 
    小的函数更容易加以组合形成新的功能。
  • 易于调试 
    细化的、定义清晰的函数使得调试更加简单。当程序不正常运行时,每一个函数都是检查数据是否正确的接口,能更快速地排除没有问题的代码,定位到出现问题的地方。
  • 易于测试 
    不依赖于系统状态的函数无须在测试前构造测试桩,使得编写单元测试更加容易。
  • 更高的生产率 
    函数式编程产生的代码比其他技术更少(往往是其他技术的一半左右),并且更容易阅读和维护。
1.3. 如何辨认函数式风格?

支持函数式编程的语言通常具有如下特征,大量使用这些特征的代码即可被认为是函数式的:

  • 函数是一等公民 
    函数能作为参数传递,或者是作为返回值返回。这个特性使得模板方法模式非常易于编写,这也促使了这个模式被更频繁地使用。 
    以一个简单的集合排序为例,假设lst是一个数集,并拥有一个排序方法sort需要将如何确定顺序作为参数。 
    如果函数不能作为参数,那么lst的sort方法只能接受普通对象作为参数。这样一来我们需要首先定义一个接口,然后定义一个实现该接口的类,最后将该类的一个实例传给sort方法,由sort调用这个实例的compare方法,就像这样:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #伪代码
    interface Comparator {
        compare(o1, o2)
    }
    lst = list(range(5))
    lst.sort(Comparator() {
        compare(o1, o2) {
            return o2 - o1 //逆序
    })

    可见,我们定义了一个新的接口、新的类型(这里是一个匿名类),并new了一个新的对象只为了调用一个方法。如果这个方法可以直接作为参数传递会怎样呢?看起来应该像这样:

    ?
    1
    2
    3
    4
    def compare(o1, o2):
        return o2 - o1 #逆序
    lst = list(range(5))
    lst.sort(compare)

    请注意,前一段代码已经使用了匿名类技巧从而省下了不少代码,但仍然不如直接传递函数简单、自然。

  • 匿名函数(lambda) 
    lambda提供了快速编写简单函数的能力。对于偶尔为之的行为,lambda让你不再需要在编码时跳转到其他位置去编写函数。 
    lambda表达式定义一个匿名的函数,如果这个函数仅在编码的位置使用到,你可以现场定义、直接使用:

    ?
    1
    lst.sort(lambda o1, o2: o1.compareTo(o2))

    相信从这个小小的例子你也能感受到强大的生产效率:)

  • 封装控制结构的内置模板函数 
    为了避开边界效应,函数式风格尽量避免使用变量,而仅仅为了控制流程而定义的循环变量和流程中产生的临时变量无疑是最需要避免的。 
    假如我们需要对刚才的数集进行过滤得到所有的正数,使用指令式风格的代码应该像是这样:

    ?
    1
    2
    3
    4
    lst2 = list()
    for i in range(len(lst)): #模拟经典for循环
        if lst[i] > 0:
            lst2.append(lst[i])

    这段代码把从创建新列表、循环、取出元素、判断、添加至新列表的整个流程完整的展示了出来,俨然把解释器当成了需要手把手指导的傻瓜。然而,“过滤”这个动作是很常见的,为什么解释器不能掌握过滤的流程,而我们只需要告诉它过滤规则呢? 
    在Python里,过滤由一个名为filter的内置函数实现。有了这个函数,解释器就学会了如何“过滤”,而我们只需要把规则告诉它:

    ?
    1
    lst2 = filter(lambda n: n > 0, lst)

    这个函数带来的好处不仅仅是少写了几行代码这么简单。 
    封装控制结构后,代码中就只需要描述功能而不是做法,这样的代码更清晰,更可读。因为避开了控制结构的干扰,第二段代码显然能让你更容易了解它的意图。 
    另外,因为避开了索引,使得代码中不太可能触发下标越界这种异常,除非你手动制造一个。 
    函数式编程语言通常封装了数个类似“过滤”这样的常见动作作为模板函数。唯一的缺点是这些函数需要少量的学习成本,但这绝对不能掩盖使用它们带来的好处。

  • 闭包(closure) 
    闭包是绑定了外部作用域的变量(但不是全局变量)的函数。大部分情况下外部作用域指的是外部函数。 
    闭包包含了自身函数体和所需外部函数中的“变量名的引用”。引用变量名意味着绑定的是变量名,而不是变量实际指向的对象;如果给变量重新赋值,闭包中能访问到的将是新的值。 
    闭包使函数更加灵活和强大。即使程序运行至离开外部函数,如果闭包仍然可见,则被绑定的变量仍然有效;每次运行至外部函数,都会重新创建闭包,绑定的变量是不同的,不需要担心在旧的闭包中绑定的变量会被新的值覆盖。 
    回到刚才过滤数集的例子。假设过滤条件中的 0 这个边界值不再是固定的,而是由用户控制。如果没有闭包,那么代码必须修改为:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class greater_than_helper:
        def __init__(self, minval):
            self.minval = minval
        def is_greater_than(self, val):
            return val > self.minval
    def my_filter(lst, minval):
        helper = greater_than_helper(minval)
        return filter(helper.is_greater_than, lst)

    请注意我们现在已经为过滤功能编写了一个函数my_filter。如你所见,我们需要在别的地方(此例中是类greater_than_helper)持有另一个操作数minval。 
    如果支持闭包,因为闭包可以直接使用外部作用域的变量,我们就不再需要greater_than_helper了:

    ?
    1
    2
    def my_filter(lst, minval):
        return filter(lambda n: n > minval, lst)

    可见,闭包在不影响可读性的同时也省下了不少代码量。 
    函数式编程语言都提供了对闭包的不同程度的支持。在Python 2.x中,闭包无法修改绑定变量的值,所有修改绑定变量的行为都被看成新建了一个同名的局部变量并将绑定变量隐藏。Python 3.x中新加入了一个关键字 nonlocal 以支持修改绑定变量。但不管支持程度如何,你始终可以访问(读取)绑定变量。

  • 内置的不可变数据结构 
    为了避开边界效应,不可变的数据结构是函数式编程中不可或缺的部分。不可变的数据结构保证数据的一致性,极大地降低了排查问题的难度。 
    例如,Python中的元组(tuple)就是不可变的,所有对元组的操作都不能改变元组的内容,所有试图修改元组内容的操作都会产生一个异常。 
    函数式编程语言一般会提供数据结构的两种版本(可变和不可变),并推荐使用不可变的版本。
  • 递归 
    递归是另一种取代循环的方法。递归其实是函数式编程很常见的形式,经常可以在一些算法中见到。但之所以放到最后,是因为实际上我们一般很少用到递归。如果一个递归无法被编译器或解释器优化,很容易就会产生栈溢出;另一方面复杂的递归往往让人感觉迷惑,不如循环清晰,所以众多最佳实践均指出使用循环而非递归。 
    这一系列短文中都不会关注递归的使用。

Python函数式编程指南相关推荐

  1. Python函数式编程指南(一):概述

    这大概算是Python最难啃的一块骨头吧.在我Python生涯的这一年里,我遇到了一些Pythoner,他们毫无例外地完全不会使用函数式编程(有些人喜欢称为Pythonic),比如,从来不会传递函数, ...

  2. Python函数式编程指南(二):函数

    这是此系列的第二篇,试图说明在Python中如何更好地使用函数并引导诸位使用函数式的思维进行思考.掌握并应用这些内容,就已经是至少形似的函数式风格的代码了,至于思维么,这个真靠自己. 作者水平有限,如 ...

  3. 《Python面向对象编程指南》——1.2 基类中的__init__()方法

    本节书摘来自异步社区<Python面向对象编程指南>一书中的第1章,第1.2节,作者[美]Steven F. Lott, 张心韬 兰亮 译,更多章节内容可以访问云栖社区"异步社区 ...

  4. 函数式编程语言python-10分钟学会python函数式编程

    原标题:10分钟学会python函数式编程 在这篇文章里,你将学会什么是函数范式以及如何使用Python进行函数式编程.你也将了解列表推导和其它形式的推导. 函数范式 在命令式范式中,通过为计算机提供 ...

  5. Python函数式编程——map()、reduce()

    提起map和reduce想必大家并不陌生,Google公司2003年提出了一个名为MapReduce的编程模型[1],用于处理大规模海量数据,并在之后广泛的应用于Google的各项应用中,2006年A ...

  6. python函数式编程读取数据-python学习笔记9:函数式编程

    函数式编程(FunctionalProgramming) 基于lambda演算的一种编程方式 程序中只有函数 函数可以作为参数,同样可以作为返回值 纯函数式编程语言: LISP, Haskell Py ...

  7. python编写函数_浅谈Python 函数式编程

    匿名函数lambda表达式 什么是匿名函数? 匿名函数,顾名思义就是没有名字的函数,在程序中不用使用 def 进行定义,可以直接使用 lambda 关键字编写简单的代码逻辑.lambda 本质上是一个 ...

  8. Python函数式编程简介(二)返回函数

    本文为Python函数式编程的第二部分主要讲解返回函数和闭包 2.返回函数 2.1.函数作为返回值 高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回. 我们来实现一个可变参数的求和.通常 ...

  9. Python函数式编程简介(一)高阶函数

    本文概括介绍Python函数式编程的一些概念及用法,详细请参考: https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df ...

最新文章

  1. linux 命令行编辑常用快捷键
  2. 关于UAC执行级别的研究
  3. P2619 [国家集训队]Tree I(WQS二分/带权二分/最小生成树)
  4. SecureCRT 用来当串口工具的设置
  5. mac安装多php环境变量配置,Mac如何安装多个php版本
  6. 【Linux】线程同步之信号量同步
  7. Android Studio安装Flutter
  8. 小程序获取城市经纬度_微信小程序获取当前所在城市的方法
  9. C++11 enable_shared_from_this
  10. echarts在(React,Vue)中的使用总结
  11. Filter的敏感词汇过滤案例
  12. 电子科学与技术毕业论文题目【452个】
  13. 空间频率 MTF和 SFR
  14. 基于ssm的超市进销存管理系统
  15. php中eregi,PHP 函数 eregi()
  16. php redis 防超卖,redis防止抢购商品超卖
  17. 6月13日云栖精选夜读:数梦工场完成A轮7.5亿融资 三个维度构建“新型互联网”
  18. 穆迪分析宣布推出云服务,助力银行实现监管合规
  19. 性价比哪家强?富勒G93S光磁微动鼠标深度评测
  20. signature=5beb9c95e4bde36fb7ac6f12f6f6f1e5,MS12-055:Windows 内核模式驱动程序中的漏洞可能会允许特权提升:2012 年 8 月 14 日...

热门文章

  1. 【小白学习keras教程】三、Kears中常见模型层Padding、Conv2D、MaxPooling2D、Flatten和Dense
  2. DataFountain训练赛 | 用户逾期行为预测
  3. pytorh 激活函数,循环神经网络层,损失函数和优化器
  4. myFocus制作焦点图
  5. 京东 | AI人才联合培养计划
  6. 如何用最简单的方式理解傅立叶变换?
  7. 让Keras更酷一些:中间变量、权重滑动和安全生成器
  8. 5 篇 AAAI 2018 论文看「应答生成」
  9. AI学习与进阶实践-基于行业价值的AI学习与进阶路径
  10. spyder的输出面板不小心关掉了怎么办?