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="\n") # 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) + "\n" + 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) + "\n" + 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.函数式编程

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

posted @ 2016-09-20 23:40  Eliefly  阅读(1343)  评论(0编辑  收藏  举报