OutOfMemory.CN技术专栏-> Python-> 函数式编程实用介绍（2）

# 函数式编程实用介绍（2）

``````from random import random
def move_cars():
for i, _ in enumerate(car_positions):
if random() > 0.3:
car_positions[i] += 1
def draw_car(car_position):
print '-' * car_position
def run_step_of_race():
global time
time -= 1
move_cars()
def draw():
print ''
for car_position in car_positions:
draw_car(car_position)
time = 5
car_positions = [1, 1, 1]
while time:
run_step_of_race()
draw()
``````

``````from random import random
def move_cars(car_positions):
return map(lambda x: x + 1 if random() > 0.3 else x,
car_positions)
def output_car(car_position):
return '-' * car_position
def run_step_of_race(state):
return {'time': state['time'] - 1,
'car_positions': move_cars(state['car_positions'])}
def draw(state):
print ''
print '\n'.join(map(output_car, state['car_positions']))
def race(state):
draw(state)
if state['time']:
race(run_step_of_race(state))
race({'time': 5,
'car_positions': [1, 1, 1]})
``````

``````def zero(s):
if s[0] == "0":
return s[1:]
def one(s):
if s[0] == "1":
return s[1:]
``````

` zero() ` 接受一个字符串 ` s ` 作为参数。如果该参数的第一个字符是 ` '0' ` ，则函数返回余下的字符串；如果不是，则返回 ` None ` ，即 Python 函数的默认返回值。 ` one() ` 函数功能一样，只不过用于判断的字符换成了 ` '1' `

``````print rule_sequence('0101', [zero, one, zero])
# => 1
print rule_sequence('0101', [zero, zero])
# => None
``````

``````def rule_sequence(s, rules):
for rule in rules:
s = rule(s)
if s == None:
break
return s
``````

``````def rule_sequence(s, rules):
if s == None or not rules:
return s
else:
return rule_sequence(rules[0](s), rules[1:])
``````

## 使用管道

``````bands = [{'name': 'sunset rubdown', 'country': 'UK', 'active': False},
{'name': 'women', 'country': 'Germany', 'active': False},
{'name': 'a silver mt. zion', 'country': 'Spain', 'active': True}]
def format_bands(bands):
for band in bands:
band['name'] = band['name'].replace('.', '')
band['name'] = band['name'].title()
format_bands(bands)
print bands
# => [{'name': 'Sunset Rubdown', 'active': False, 'country': 'Canada'},
#     {'name': 'Women', 'active': False, 'country': 'Canada' },
#     {'name': 'A Silver Mt Zion', 'active': True, 'country': 'Canada'}]
``````

``````print pipeline_each(bands, [set_canada_as_country,
strip_punctuation_from_name,
capitalize_names])
``````

` pipeline_each() ` 作业每次传递一个乐队到一个转换函数中，比如 ` set_canada_as_country() ` 。在函数应用于所有乐队之后， ` pipeline_each() ` 函数捆绑所有转换后的乐队，然后，它传递每一个乐队到下一个函数中。

``````def assoc(_d, key, value):
from copy import deepcopy
d = deepcopy(_d)
d[key] = value
return d
def strip_punctuation_from_name(band):
return assoc(band, 'name', band['name'].replace('.', ''))
def capitalize_names(band):
return assoc(band, 'name', band['name'].title())
``````

Python 的字符串和字典在可变性方面的反差，充分展示了如 Clojure 之类语言的魅力。程序员再也不需要为他们是否改变了数据而担心，答案当然是否定的。

``````def pipeline_each(data, fns):
return reduce(lambda a, x: map(x, a),
fns,
data)
``````

``````set_canada_as_country = call(lambda x: 'Canada', 'country')
strip_punctuation_from_name = call(lambda x: x.replace('.', ''), 'name')
capitalize_names = call(str.title, 'name')
strip_punctuation_from_name,
capitalize_names])
``````

``````print pipeline_each(bands, [call(lambda x: 'Canada', 'country'),
call(lambda x: x.replace('.', ''), 'name'),
call(str.title, 'name')])
``````

` call() ` 函数代码：

``````def assoc(_d, key, value):
from copy import deepcopy
d = deepcopy(_d)
d[key] = value
return d
def call(fn, key):
def apply_fn(record):
return assoc(record, key, fn(record.get(key)))
return apply_fn
``````

``````def extract_name_and_country(band):
plucked_band = {}
plucked_band['name'] = band['name']
plucked_band['country'] = band['country']
return plucked_band
print pipeline_each(bands, [call(lambda x: 'Canada', 'country'),
call(lambda x: x.replace('.', ''), 'name'),
call(str.title, 'name'),
extract_name_and_country])
# => [{'name': 'Sunset Rubdown', 'country': 'Canada'},
#     {'name': 'A Silver Mt Zion', 'country': 'Canada'}]
``````

` extract_name_and_country() ` 可以写成一个通用的函数 ` pluck() `` pluck() ` 可以这样用：

``````print pipeline_each(bands, [call(lambda x: 'Canada', 'country'),
call(lambda x: x.replace('.', ''), 'name'),
call(str.title, 'name'),
pluck(['name', 'country'])])
``````

``````def pluck(keys):
def pluck_fn(record):
return reduce(lambda a, x: assoc(a, x, record[x]),
keys,
{})
return pluck_fn
``````

## 接下来呢？

(1) 不可变数据是指不能被改变的数据。一些语言如 Clojure，默认所有值为不可变数据。任何“改变”操作都是基于原值的拷贝进行，首先复制一个拷贝，然后改变拷贝，最后传回这个更改的拷贝。程序可能会陷入程序员不完备的可能状态模型，这样做可以消除由此引发的错误。

(2) 所有将函数视为一等公民，对函数和其它值一视同仁的语言。这就意味着、你不仅可以创建函数，你还可以将函数作为参数传递给函数，作为返回值从函数中返回，存储在数据结构中。

(3) 尾部调用优化是一种编程语言的特性。每一次递归调用，都会产生一个新堆栈帧，用于为当前调用存储参数和本地变量。如果一个函数递归调用很多次，很可能会导致解释器或编译器内存溢出。具备尾部调用优化功能的语言，对于整个序列的递归调用，重用同一个堆栈帧。像 Python 之类的语言没有尾部调用优化这一特性，因此限制了一个函数可以递归调用的次数只能数千次。在 ` race() ` 函数中，只有区区5次调用，因此毫无问题。

(4) 柯里化的意思是，把接受多个参数的函数转换成接受第一个参数作为（唯一）参数的函数，并且返回接受第二个参数作为（唯一）参数的新函数，其余以此类推。

(5) 并行化意味着同时运行相同的代码而无需同步。这些并发进程通常运行在多个处理器上。

(6) 惰性求值是一项编译器技术。其目的是将代码运行推迟到实际需要这段代码的最终结果的时候。

(7) 如果每次运行都能生成同样的结果，那么这个进程就具有确定性。

-全文结束-

1

0

Qingniu：我有明珠一颗，久被尘劳关锁，今朝尘尽光生，照破山河万朵。 柴陵郁禅师（宋）