• Python 函数式编程


    1.什么是函数式编程

    Wiki上对Functional Programming的定义:

    In computer science, functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data. It is a declarative programming paradigm, which means programming is done with expressions[1] or declarations[2] instead of statements. In functional code, the output value of a function depends only on the arguments that are input to the function, so calling a function f twice with the same value for an argument x will produce the same result f(x) each time.** Eliminating side effects, i.e. changes in state that do not depend on the function inputs, can make it much easier to understand and predict the behavior of a program, which is one of the key motivations for the development of functional programming. **

    "函数式编程"是一种"编程范式"(programming paradigm),一种构建计算机程序结构和元素的风格/模式。它把运算当作是数据函数的计算,避免了多变的状态和易变的数据。它是一种声明性编程范式,意味着程序是由表达式或声明完成的而不是语句。在函数代码中,函数的输出值只依赖于对这个函数的输入,因此以同样的参数值x调用函数f两次,每次得到的结果是相同的f(x)。消除副作用,即状态的改变不再依赖于函数的输入,这使理解和预测程序的行为更加容易,这也是发展函数式编程的主要动力。****

    所谓"副作用"(side effect),指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。
    函数式编程强调没有"副作用",意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。

    和声明性编程对应的是命令式编程(imperative programming), 它在源程序中使用命令改变状态,最简单的例子就是赋值。存在的副作用是可能改变程序状态的值。因此函数没有返回值是有意义的。由于它们缺少引用透明性(referential transparency),即根据程序的执行状态在不同的时间,相同的表达式可能会产生不同的结果。

    引用透明(Referential transparency),指的是函数的运行不依赖于外部变量或"状态",只依赖于输入的参数,任何时候只要参数相同,引用函数所得到的返回值总是相同的。

    函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量(应该也存在不是纯粹的函数式编程,使用函数式编程解决局部问题,同时也存在变量),因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。

    函数式编程就是如何编写程序的方法论。
    它属于"结构化编程"的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。举例来说,现在有这样一个数学表达式:

    (1 + 2) * 3 - 4
    

    传统的过程式编程,可能这样写:

    var a = 1 + 2;
    var b = a * 3;
    var c = b - 4;
    

    函数式编程要求使用函数,我们可以把运算过程定义为不同的函数,然后写成下面这样:

    var result = subtract(multiply(add(1,2), 3), 4);
    

    这就是函数式编程。

    2.函数即变量(first class function)

    Python中一切皆为对象。函数也不例外,且函数与其他数据类型一样,处于平等地位。
    函数可以赋值给变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。函数名其实就是指向函数的变量,它可以被赋其他值。

    这样才能快捷实施函数式编程,C语言里函数与变量不平等的,或许借助函数指针倒是可以实现,但颇为麻烦。

    函数赋值给其他变量

    >>> abs(-10)
    10
    >>> foo = abs
    >>> foo(-10)
    10
    

    函数名被赋其他变量

    >>> abs
    <built-in function abs>
    >>> abs = 10
    >>> abs
    10
    

    函数作为参数传递给另一函数

    >>> def add(x, y, foo):
    ...     return foo(x) + foo(y)
    ... 
    >>> add(-10, 3, abs)
    13
    

    函数作为返回值返回

    >>> def foo():
        return abs
    ... ... 
    >>> fun = foo()
    >>> fun(-10)
    10
    
    3.匿名函数lambda

    lambda表达式用于创建匿名函数,是一种简化函数定义的快速方法。不像def那样可以执行多重语句和注释,lambda形式定义的函数没有def形式的强大。优点是快速简单,一条语句就可定义一个函数。
    lambda语法如下:

    lambda arguments: expression

    lambda定义一个求和函数add。

    add = lambda x, y: x + y
    

    效果和def形式定义的是一样的。

    def add(x, y):
        return x + y
    

    lambda表达式通常是直接嵌入在需要函数的地方,且该函数能使用一个表达式完整描述,不大会直接将lambda表达式命令成函数。这虽然很酷,但是不方便拓展,如果一开始就需要给函数命名,应该使用def形式定义。

    4.编程风格

    打印斐波那契数列前10个。

    • 命令式风格,使用了循环,有变量赋值语句。(如何解释副作用的原因?)
    def fibonacci(n, first=0, second=1):
        while n != 0:
            print(first, end="
    ") # side-effect
            n, first, second = n - 1, second, first + second # assignment
    fibonacci(10)
    
    • 函数式风格,使用了递归方式
    fibonacci = (lambda n, first=0, second=1:
        "" if n == 0 else
        str(first) + "
    " + fibonacci(n - 1, second, first + second))
    print(fibonacci(10), end="")
    

    此处的lambda表达式略微复杂,换成def的形式:

    def fibonacci(n, first=0, second=1):
        if n == 0:
            return ''
        else:
            return str(first) + "
    " + fibonacci(n - 1, second, first + second)
    print(fibonacci(10), end="")
    

    循环是在描述我们该如何地去解决问题。
    递归是在描述这个问题的定义。

    简单点理解,函数式编程就是把函数当普通的变量或对象一样使用,函数用做参数或返回值。函数自身带有逻辑计算,当变量使用可以得到非常高效的代码。

    4.高阶函数

    传统的说,Python是一种非函数式语言(non-functional),但在编码时可以使用函数式风格。Python中的lambda,高阶函数(map, reduce, filter),偏函数(partial function),闭包(closure),装饰器(decorator)都有函数式思想。通过 Python 中的高阶函数稍稍体会下函数式编程的趣味。
    把函数作为参数传入的函数,这样的函数称为高阶函数。Python中知名高阶函数有map, reduce, filter。

    • map

    | map(func, *iterables) --> map object
    |
    | Make an iterator that computes the function using arguments from
    | each of the iterables. Stops when the shortest iterable is exhausted.

    map()传入的第一个参数是函数,第二个参数是Iterable对象,即可迭代的对象,如list,dict,str都是Iterable对象。map将函数func依次作用到Iterable对象的元素上,返回一map对象。(map对象是?)
    如下将list中元素平方后得到新的list。

    >>> ret= map(lambda x: x*x, list(range(5)))
    >>> print(ret)
    <map object at 0x029AE4B0>
    >>> print(list(ret))
    [0, 1, 4, 9, 16]
    

    不是很严格的map()实现:

    from collections import Iterable
    
    def map(func, a_iterable):
        result = []
        if isinstance(a_iterable, Iterable) is False:
            raise TypeError('%s object is not iterable' % type(a_iterable))
        else:
            for item in a_iterable:
                result.append(func(item)) 
        return result
    
    • reduce
      Python3里 reduce() 在 functools 模块下。

    functools.reduce = reduce(...)
    reduce(function, sequence[, initial]) -> value

    Apply a function of two arguments cumulatively to the items of a sequence,
    from left to right, so as to reduce the sequence to a single value.
    For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates
    ((((1+2)+3)+4)+5). If initial is present, it is placed before the items
    of the sequence in the calculation, and serves as a default when the
    sequence is empty.

    reduce()最少接收2个参数,最多接收3个参数,第2个参数要求可迭代的。reduce()作用效果:
    reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

    >>> from functools import reduce
    >>> reduce(lambda x, y: x+y, [1, 2, 3], 10)
    16
    >>> reduce(lambda x, y: x+y, [1, 2, 3])
    6
    

    map() 和 reduce() 结合使用实现 str 到 int 的过程,简洁又酷炫。

    from functools import reduce
    
    def str2int(a_str):
        
        char2int = lambda x :{'0':0, '1':1, '2':2, '3':3, '4': 4, '5': 5,
             '6': 6, '7':7, '8': 8, '9': 9}[x]
    
        return reduce(lambda x, y: x*10+y, map(char2int, a_str))
    
    print(str2int('1234'))
    
    • filter

    class filter(object)
    | filter(function or None, iterable) --> filter object
    |
    | Return an iterator yielding those items of iterable for which function(item)
    | is true. If function is None, return the items that are true.

    filter()也接收一个函数和一个序列。和 map()不同的时,filter()把传入的函数依次作用于每个元素, 然后根据返回值是 True 还是 False 决定保留还是丢弃该元素。
    如得到序列中的整数:

    >>> ret = filter(lambda x: x if x>0 else None, [1, -2, 3, -4, 0])
    >>> print(ret)
    <filter object at 0x029AEEF0>
    >>> print(list(ret))
    [1, 3]
    
    • sorted

    sorted(iterable, key=None, reverse=False)
    Return a new list containing all items from the iterable in ascending order.
    |
    A custom key function can be supplied to customise the sort order, and the
    reverse flag can be set to request the result in descending order.

    sorted()函数也是一个高阶函数,它还可以接收一个 key 函数来实现自定义的排序。
    按绝对值排序

    >>> sorted([1, -2, -3, 4], key=abs)
    [1, -2, -3, 4]
    

    问题:
    有这样一个 list :['1y','3m','1m','7d','5d','1d','12h','6h','3h']
    其中 y 表示年, m 表示月, d 表示天, h 表示小时,前面的数字表示对应的时间
    问如何用 sort 在一行内按照年>月>天>小时或者反过来的顺序进行排序?

    >>> a_list = ['1y','3m','1m','7d','5d','1d','12h','6h','3h']
    >>> sorted(a_list, key = lambda x: {'y':365*24, 'm':30*24, 'd':24, 'h':1}[x[-1]] * int(x[:-1]))
    ['3h', '6h', '12h', '1d', '5d', '7d', '1m', '3m', '1y']
    

    参考:

    1.wiki-Functional programming
    2.Python函数式编程指南(一):概述
    3.函数式编程扫盲篇
    4.函数式编程初探
    5.函数式编程

    完---如有错漏,请指教:)

  • 相关阅读:

    python 爬取可用
    安装完出现Deprecated: Function ereg_replace() is deprecated in
    mysql数据库还原出错ERROR:Unknown command ‘\’解决手记
    mysql 常用语句
    This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery 解决方法
    js 中 json对象 与 json字符串 间相互转换
    神器 Sublime Text 3 的一些常用快捷键
    神器 Sublime Text 3 的一些常用插件
    apache php gzip压缩输出的实现方法
  • 原文地址:https://www.cnblogs.com/elie/p/5877625.html
Copyright © 2020-2023  润新知