斐波那契级数真是计算机教学的万用示例。f(n) = f(n-1) + f(n-2) 这种实现方案可以示范递归函数;而在算法课程中,它又成了指数级别复杂度算法的反面典型;由于值增长得很快,接下来就可以讲授如何设计一个新的数据类型来表示这些迅速增长的数值了……
今天,我们拿它来讲讲 Python 语言的一些特性,特别是对函数式编程(functional programming)的支持。
生成器与惰性计算
这其实是 Python Cookbook 第二版中的一个例子:需要一个无界的生成器,它能一次一项地生成斐波那契数的整个序列。
Python 的生成器(generator)可以完美地解决这类问题。每次调用生成器,生成器运行到 yield,然后就被冻结起来,同时保存状态;下一次调用,会从生成器被冻结的状态继续运行。生成器依赖于惰性计算(lazy evaluation),即只有在请求到来时,一项数据才会被计算出来。对于生成类的问题,尤其是无穷生成问题,生成器的实现是再合适不过了:
def fib():
x, y = 0, 1
while True:
yield x
x, y = y, x + y
可以用 itertools.islice 方法获得级数的前 n 项:
In [2]: from itertools import *
In [3]: list(islice(fib(), 10))
Out[3]: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Python 对函数式编程的支持:itertools
以生成器的形式生成斐波那契级数有什么好处呢?Python 对函数式编程有较好的支持,itertools 中提供了一系列函数,可以对以上无穷生成器进行操作,在简化写法的同时,充分发挥惰性计算的优势。
如果要得到所有1000以下的斐波那契数,可以使用 takewhile:
In [4]: list(takewhile(lambda x: x < 1000, fib()))
Out[4]: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]
第20项(从第0项开始计):
In [5]: next(islice(fib(), 20, None))
Out[5]: 6765
第一个大于100000的项:
In [6]: next(filter(lambda x: x > 100000, fib()))
Out[6]: 121393
可以看到,代码短小精悍,非常 functional,生成器 fib() 得到了最大程度的复用。
更多信息,可以阅读 Python 官方文档10.1. itertools
— Functions creating iterators for efficient looping。