Python|Python函数式编程学习(lambda, map, reduce, filter)

以前也学过函数式编程,基本也就是函数嵌套,没有深入学习。最近重写代码,重新学习了函数式编程,所以写了这篇blog。
首先介绍下函数式编程的定义:来自维基百科点击打开链接
函数式编程(Functional programming)或者函数程序设计,又称泛函编程,是一种编程范型,它将计算机运算视为数学上的函数计算,并且避免使用程序状态以及易变对象。函数编程语言最重要的基础是λ演算(lambda calculus)。而且λ演算的函数可以接受函数当作输入(引数)和输出(传出值)。
函数式编程的几个特性: 1、first class functions: 指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。
【2014.05.22更新:函数作为返回值

>>> def later_sum(*args): def sum(): k = 0 for i in args: k += i return k return sum>>> f = later_sum(1, 2, 3, 4, 5) >>> f >>> f() 15 >>> f1 = later_sum(1, 2, 3, 4, 5) >>> f1() 15 >>> f2 = later_sum(1, 2, 3, 4, 5) >>> f2() 15 >>> f1 ==f2 False >>>

可以看得出,当我们调用later_sum()时,返回的是1个函数并给它起名f(本质上为return会来的sum函数)。当我们调用f这个函数时候(f()函数名字+()为函数调用),也就是调用了内层函数sum()。所以f()的值为15,这就是 延迟使用! 像这样,函数内部又定义函数, 内部函数可以引用外部函数的参数, 调用外部函数时,外部函数的参数保存在返回的函数里,这种形式称为 闭包(Closure)。
>>> def later_sum(*args): k = 0 def sum(): for i in args: k += i return k return sum>>> f = later_sum(1, 2, 3, 4, 5) >>> f()Traceback (most recent call last): File "", line 1, in f() File "", line 5, in sum k += i UnboundLocalError: local variable 'k' referenced before assignment >>>


有的Blog说:内部函数可以引用外部函数的参数和局部变量是不对的!可以由上面代码看出。
【更新于2014.07.31:python3中可以通过nonlocal关键字彻底搞定上面的例子!

>>> def later_sum(*args): k = 0 def sum(): nonlocal k for i in args: k += i return k return sum>>> later_sum(1, 2, 3, 4, 5)() 15 >>>



还需要注意的是f1()和f2()不是一回事!


2、mmutable data: 函数式编程只返回新值,不修改变量。例如:

def add_1(ago): return ago + 1


我们定义一个变量a的值为1,我们调用add_1(a)时,a的值不会改变,函数返回值为2。也就是说add_1()不会改变外部变量的值且函数内部没有依赖外部变量的值,和外部无关,最后返回一个值给你,这就是函数式编程。


可能看到这,很多人觉得这也太简单了,我平时代码就这么写的。很显然,被N多人推崇N久的东西不会这么简单。我们继续学习。
3.Referential transparency: 引用透明,也就是说函数结果只依赖于参数的改变,参数不变,输出不会变。
以上也是总结书本和网友所说的。
再来一个简单的例子:

>>> def add(x): def x_add_y(y): return x + y return x_add_y >>> add(3)(4) 7

【Python|Python函数式编程学习(lambda, map, reduce, filter)】

这段程序什么意思呢,首先add(3)返回了另一个函数x_add_y()然后再调用x_add_y(4)。本质上就是Currying技术:把一个函数的多个参数分解成多个函数,然后把函数层层封装。这时候可能觉得这样做还是没用,可能还会造成阅读代码变困难。是的,我们应该学习下高级点的东西!这就是lambda, map, reduce, filter!


Lambda:
def name(arguments): return expression


lambda本质上是一个表达式,他定义了一个匿名函数,返回的是一个函数对象。官方文档原文:Lambda expressions (sometimes called lambda forms) have the same syntactic position as expressions. They are a shorthand to create anonymous functions; the expression lambdaarguments: expression yields a function object. The unnamed object behaves like a function object defined with

>>> func = lambda x, y: x + y >>> print type(func) >>> func(1, 2) 3



本质上,这个上面那个add()没有区别,都是做x+y的运算。lambda简化了函数的书写,一行代码做了前面4行代码的事,代码看起来更简洁了。也不用担心代码不能复用,上面的func(1, 2)就是很好的证明,你当然可以用func(10010, 10086)了。他的作用不限如此,在map, reduce, filter中的应用我们就可以看得到!
以下例子内容来源于官方文档:点击打开链接 更进一步学习推荐一篇论文:MapReduce: Simplified Data Processing on Large Clusters
Map map(function, sequence) calls function(item) for each of the sequence's items and returns a list of the return values. For example, to compute some cubes:

>>> map(lambda x: x*x*x, range(1, 11)) [1, 8, 27, 64, 125, 216, 343, 512, 729, 1000] >>>

More than one sequence may be passed; the function must then have as many arguments as there are sequences and is called with the corresponding item from each sequence (or None if some sequence is shorter than another). If None is passed for the function, a function returning its argument(s) is substituted.
Combining these two special cases, we see that map(None, list1, list2) is a convenient way of turning a pair of lists into a list of pairs. For example:

>>> seq = range(8) >>> map(None, seq, map(lambda x: x*x, seq)) [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25), (6, 36), (7, 49)] >>>

也就是说map(function, sequence).是把sequence中的值当参数逐个传给function,返回一个包含函数执行结果的list。如果function有两个参数,即map(function, sequence1, sequence2)。
>>> map(lambda x, y: x + y, range(9), range(9)) [0, 2, 4, 6, 8, 10, 12, 14, 16] >>> map(lambda x: x*2, range(9)) [0, 2, 4, 6, 8, 10, 12, 14, 16] >>>

Reduce reduce(function, sequence) returns a single value constructed by calling the (binary) function on the first two items of the sequence, then on the result and the next item, and so on. For example, to compute the sum of the numbers 1 through 10:

>>> reduce(lambda x, y: x+y, range(1, 11)) 55 >>>

If there's only one item in the sequence, its value is returned; if the sequence is empty, an exception is raised.
A third argument can be passed to indicate the starting value. In this case the starting value is returned for an empty sequence, and the function is first applied to the starting value and the first sequence item, then to the result and the next item, and so on. For example,

>>> def sum(seq): ...return reduce(lambda x, y: x+y, seq, 0) ... >>> sum(range(1, 11)) 55 >>> sum([]) 0 >>>

reduce需要2个参数,一个是函数,一个是sequence。数列为空时,会报错,数列正确时,会返回计算后的值,可以有第三个参数,作为starting value,也就是说函数值最终都要加上这个start value。以下例子为证:
>>> reduce(lambda x, y: x+y, range(5), 100) 110 >>> reduce(lambda x, y: x+y, range(5)) 10 >>> reduce(lambda x, y: x+y, range(5), 10086) 10096 >>>

reduce工作机制是,前面的fuction必须是需要两个参数的函数!因为reduce本身就是把sequence的前两个数传递进function并把function的返回值和第三个数再传递进fuction,类推!最后只返回一个结果! Filter filter(function, sequence) returns a sequence (of the same type, if possible) consisting of those items from the sequence for which function(item) is true. For example, to compute some primes:

>>> filter(lambda x: x%2 != 0 and x%3 != 0, range(2, 25)) [5, 7, 11, 13, 17, 19, 23] >>>

filiter返回值和sequence相关!如果function返回值是True,则添加进结果list中,当然了不一定是list,这要看sequence是什么!以下是我写的例子:

>>> filter(lambda x: len(x) != 0, 'python') 'python' # str >>> filter(lambda x: len(x) != 0, ['python']) ['python'] # list >>> filter(lambda x: len(x) != 0, ('python')) 'python' # str >>> filter(lambda x: len(x) != 0, ('python',)) ('python',) # tuple

【更新于:2014.07.05
Construct a list from those elements of iterable for which function returns true. iterable may be either a sequence, a container which supports iteration, or an iterator.If iterable is a string or a tuple, the result also has that type; otherwise it is always a list. If function is None, the identity function is assumed, that is, all elements of iterable that are false are removed.】
总结: 1、函数式编程在数学上应用很犀利!
2、代码变简单了,虽然刚开始看起来很别扭。
3、再也不用考虑循环体中各种变量了。
4、我只用表达,我是要干什么,而不是我怎么做的。

    推荐阅读