转载自:
http://wiki.woodpecker.org.cn/moin/fp4python
还是全文整体转过来看比较舒服
(其中,修改了三四处错别字)
1. 前言
本文的写作计划源于2004年9月 GreyRoar 的提议, 可惜真正动手却推到了现在, 在近一年的时间里, python的函数编程也发生了一些改变, python3000计划和最近的已经出现的"匿名块"等都使得python在这个方面的发展充满着不确定因素, 本文只讨论目前已知的能用的功能, 不保证满足将来可能发生的变化. 另外在一切开始前, 记住Python和数学里一样记号只是记号.
2. 函数式编程的概念
函数编程源自 Lisp 等高阶语言, 他们以λ演算为基础. 所谓λ演算其实指基于算子λ的一个演算体系. 他的特殊性在于, 存在一个算子 λ (λ只是记号, 没有更多含义),他的作用是将运算式子变成函数 . 例如: λx: x+1 就将 x+1这个运算式子, 变成了关于x的一个函数, 他的值是x+1. 光有λ算子构成不了完整的λ运算体系, 还需要一些辅助算子, 但是λ演算体系的基础就是这个算子. 要完全了解这个体系则需要一环扣一环的定义和定理, 太复杂这里不再详述(书丢学校了, 回校有时间补齐 ).
3. python中的函数编程
3.1. lambda
和数学中的 λ 一样, python的函数编程体系中有一个 lambda 语句, 用来生成函数, 一般这样的函数称为匿名函数.
例:就生成了一个函数 λ(x) = x+1. 可以在python的解释器中试试:
- >>> lambda x: x+1 # 生成一个匿名函数 λ(x) = x+1
- <function <lambda> at 0x00C99770>
- >>> f = lambda x: x+1 # 将这个函数绑定名字 'f'
- >>> f(1) # 调用 'f'
- 2
复制代码 lambda的具体语法是: lambda [parameter_list]: expression
parameter_list 是参数表, expression 是后面跟的表达式,
lambda本身是个运算符, 作用在这两个元素上产生一个匿名函数, 类似于以下函数定义:
- def name(parameter_list):
- return expression
复制代码
expression 是一个合法的python表达式, 显然 expression 中的变量量除了在 parameter_list 中的外必须是已知的. 注意experssion中是不能用print语句的, 要想产生输出的话可以用sys.stdout.write()等函数.
(下面会不断给出 lambda 的例子 )
3.2. 高阶函数
所谓的高阶函数其实是一组以函数为参量的函数. 下面介绍 Python 中高阶函数的具体形式.
3.2.1. map
map是最基本的函数, 搞懂了map其他函数就很容易搞懂.
map( function, list, ...)
将函数function作用到 list 的每个元素上, 将结果组成一个列表返回. 如果参数列表有多个, 函数应该是有多参数的, 每个分量取各列表上的对应值, 如果有列表比其他列表短, 不足部分当作 None . 这里list可以是任意序列(sequence), 例如列表(list) , 元组(tuple)等.
例:- >>> a = range(5);
- >>> b = range(4);
- >>> map(lambda x, y: (x, y) , a, b) # lambda x,y 接受两个参数, 生成个包含这两个参数的tuple.
- [(0, 0), (1, 1), (2, 2), (3, 3), (4, None)]
复制代码 另一种多参数调用的方法是组合列表(这也是我本人最喜欢用的方法):
map(lambda (arg1, ..., argn): expression, multiple_list)
multiple_list 是一个列表, 他的元素还是一个列表, 即参数列表. 这种方法实质还是单参数调用(这个不一定要完全理解).
例:- >>> ml = [[0, 1], [2, 3], [3, 4]]
- >>> map(lambda (x,y): x+y , ml) # lambda (x,y): x+y 接受一个有两元元组(tuple), 计算他们的和.
- [1, 5, 7]
复制代码 如果map 的第一个参数, 即function等于None, 则默认为恒等变换.
例:- >>> a = range(3)
- >>> map(None , a)
- [0, 1, 2]
复制代码 3.2.2. reduce
reduce( function, sequence[, initializer])
将函数function从左到右作用在序列的每个元素上, 函数function为二元函数, 第一个变量为上次计算所得结果,第二个变量为列表元素.
例:- >>> reduce(lambda x,y: x+y , [1,2,3,4,5]) # 相当于 ((((1+2)+3)+4)+5)
- 15
复制代码 整个计算过程就像列表在不断缩减一样.
initializer为初值.
例:- >>> reduce(lambda x,y: x+y , [1,2,3,4,5], 10) # 相当于(((((10+1)+2)+3)+4)+5)
- 25
复制代码 3.2.3. filter
filter( function, list)
从list的元素中构造一个列表, 这个列表的元素依次是list中使function为真的元素. list可以是任意序列, 如列表(List)和元组(Tuple)等, 或支出迭代子的容器, 或者迭代子(有关容器,迭代子等概念将在下面介绍,)
例:- >>> filter(lambda x: x>5 , [10,1,5,6,7]) # 将列表中小于5的元素滤去.
- [10, 6, 7]
复制代码 和map一样, 若function是None , 则代为恒等函数, 这样将滤去所有取值为false的元素(包括0和None).
4. 列表运算
目前 Guido 对Python的函数式编程很不满意, 它计划在Python3000中去除这些函数, 取而代之的是列表运算. 这些方法在当前的Python版本中也是可用的.
基本形式是 [expression for expression_list in testlist ]
和前面一样 testlist 是一个序列或者迭代子,expression是一个表达式,expression_list 是这个表达式中的参量列表(实质上是解开一个元组).
例:- >>> [ x+y for x,y in [(1,2), (2,3), (4,5)]]
- [3, 5, 9]
- >>> [ item[0]+item[1] for item in [(1,2), (2,3), (4,5)]] # 和前面的等价
- [3, 5, 9]
复制代码 for 语句可以套用:- >>> [ (i,j) for i in range(5) for j in range(3)] # 这个例子可以看出靠近expression的循环是内层循环
- [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2), (3, 0), (3, 1), (3, 2),
- (4, 0), (4, 1), (4, 2)]
复制代码 for后面还可以跟if语句, 这样相当于一个增强的 filter
例:- >>> [ i for i in range(10) if i%2==0] # 选出所有偶数
- [0, 2, 4, 6, 8]
复制代码 同样 if 也可以嵌套, 并可以与for混合嵌套 (但是, 显然过多的嵌套容易使语句变得晦涩难懂) :- >>> [ (i,j) for i in range(10) if i%2==0 for j in range(10) if j%3 == 0]
- [(0, 0), (0, 3), (0, 6), (0, 9), (2, 0), (2, 3), (2, 6), (2, 9), (4, 0), (4, 3), (4, 6), (4, 9),
- (6, 0), (6, 3), (6, 6), (6, 9), (8, 0), (8, 3), (8, 6), (8, 9)]
复制代码 4.1. 基本方法综述
前面很粗浅得讲了一下Python中函数编程的最基本内容, 下面将列出一些常用的函数编程方法, 这些方法可以大大简化你的代码.
4.2. 应用实例
编程这东西如果理解原理, 不管用那种方法写程序其实都是一样的, 函数编程只是一种方法, 用好了可以简化程序.
4.2.1. 算24点的程序
这只是一个用了点函数编程方法随手写的程序, 1年前的东西, 不是算24点的最好方法, 拿到这里, 只是展示一下函数编程的用法.
funs = [ lambda x, item: (x+item[0],
str(x)+'+('+item[1]+')'
),
lambda x, item: (x-item[0],
str(x)+'-('+item[1]+')'
),
lambda x, item: (item[0]-x,
'('+item[1]+')-'+str(x)
),
lambda x, item: (x*item[0],
str(x)+'*('+item[1]+')'
),
lambda x, item: (item[0]==0 and (0,'ZZZ')) or
(x/item[0],
str(x)+'/('+item[1]+')'
),
lambda x, item: (x==0 and (0,'ZZZ')) or
(item[0]/x,
'('+item[1]+')/'+str(x)
)
]
def con(num):
l = len(num)
p = list()
if l==1: return {num[0]:str(num[0])}
for i in range(l):
for f in funs:
p += map(lambda item: f(num[i],item),
con(num[:i]+num[i+1:]).items()
)
return dict(p)
print con(map(float,[1,5,6,7])).get(21.0,0)
|
5. 函数编程的缺陷
函数编程最明显的缺陷是易用性不够, 对于大多数初学者来说, 函数编程是不容易理解的, 写出正确的函数式程序是有困难的.
同样,函数式编程的形式中含有很多的嵌套,这也使程序看起来很晦涩, 这正是 Guido 计划取消Python中函数式编程特性的原因之一.
[ 本帖最后由 izhier 于 2009-4-25 17:12 编辑 ] |