接上文,我们定义了判断某行是否file copy,或者file overwrite的两个函数,事实上我们如果使用
startswith函数代替slice,会更稳定。从函数式编程角度,我们给出下面几个表达式:
isRegDBRoot = lambda line: line[:11]=='RegDB Root:'
isRegDBKey = lambda line: line[:10]=='RegDB Key:'
isRegDBVal = lambda line: line[:10]=='RegDB Val:'
有了这三个更加紧凑的函数,我们给出之前方法的函数式编程写法;
lines = open(r'd:\python22\install.log').readlines()
regroot_lines = filter(isRegDBRoot, lines)
如果,你想通过多个选择标准来选择相应的行,那么函数式的写法就会显得笨拙。
我们可以根据相似的思路写出一个给定要求对应的过滤函数如下:
#*--------------- Find the RegDB lines ------------------#
def isAnyRegDB(line):
if line[:11]=='RegDB Root:': return 1
elif line[:10]=='RegDB Key:': return 1
elif line[:10]=='RegDB Val:': return 1
else: return 0
但是,如果需求变成了其他的组合呢?显然依据这个思路,我们必须为另外的组合写更多的组合,这种组合是爆炸性的增长的。。。另外的思路是通过嵌套过滤函数来达到:
#*------------- Filter on two line predicates -----------#
shortline = lambda line: len(line) < 25
short_regvals = filter(shortline, filter(isRegDBVal, lines))
这样的思路写出的程序比较容易排错,该函数使用了两个已有的函数来生成新的程序。但是一个显而易见的问题是:这样的程序很难阅读。同样的问题也出现在map函数上。通常解决这样问题的方法是多增加一些循环和中间变量。。
根据函授式编程的思路,我们可以在不增加代码量的情况下解决上面描述的问题。
这里的关键点是:选择的生成一些高级别的组合函数:这里的高级别函数是指:那些接受函数作为输入,返回函数作为输出的函数。一级函数接受数据,返回数据。相对应的一个高级函数的输入和输出都是函数对象。这些函数对象将在未来被其他地方使用:高级函数的一个例子是;函数工厂,它返回一个或者一组函数对象。下面是一个加法函数工厂;
>>> def adder_factory(n):
... return lambda m, n=n: m+n
...
>>> add10 = adder_factory(10)
>>> add10
<function <lambda> at 0x00FB0020>
>>> add10(4)
14
>>> add10(20)
30
>>> add5 = adder_factory(5)
>>> add5(4)
9
回到正题,我们提到用高级组合函数来解决前面提到的问题,这些高级函数通过接受一些函数,对这些函数对象进行操作从而合成其他的函数。下面是一个组合高级函数库:
#------------------- combinatorial.py -------------------#
from operator import mul, add, truth
apply_each = lambda fns, args=[]: map(apply, fns, [args]*len(fns))
bools = lambda lst: map(truth, lst)
bool_each = lambda fns, args=[]: bools(apply_each(fns, args))
conjoin = lambda fns, args=[]: reduce(mul, bool_each(fns, args))
all = lambda fns: lambda arg, fns=fns: conjoin(fns, (arg,))
both = lambda f,g: all((f,g))
all3 = lambda f,g,h: all((f,g,h))
and_ = lambda f,g: lambda x, f=f, g=g: f(x) and g(x)
disjoin = lambda fns, args=[]: reduce(add, bool_each(fns, args))
some = lambda fns: lambda arg, fns=fns: disjoin(fns, (arg,))
either = lambda f,g: some((f,g))
anyof3 = lambda f,g,h: some((f,g,h))
compose = lambda f,g: lambda x, f=f, g=g: f(g(x))
compose3 = lambda f,g,h: lambda x, f=f, g=g, h=h: f(g(h(x)))
ident = lambda x: x
这样通过使用这些高级组合函数:我们可以这样解决之前遇到的问题:
#----- Some examples using higher-order functions -----#
# Don't nest filters, just produce func that does both
short_regvals = filter(both(shortline, isRegVal), lines)
# Don't multiply ad hoc functions, just describe need
regroot_lines = \
filter(some([isRegDBRoot, isRegDBKey, isRegDBVal]), lines)
# Don't nest transformations, make one combined transform
capFlipNorm = compose3(upper, flip, normalize)
cap_flip_norms = map(capFlipNorm, lines)
可以看出通过函数式编程方法,我们可以不仅获得更加精炼的代码,以及更好的可读性.
任何嵌套的filter 和map函数都可以通过高级组合函数进行化简,总的来说,函数式编程方法得到的代码量应该在常规方法代码量的一半。
这种高级组合函数的另外一个好处是;他们可以提供一整套的函数的布尔表达式。
#*---------- Simple Boolean algebra of values ----------#
satisfied = (this or that) and (foo or bar)
在文本处理环境下,这样的操作经常是一些predic函数的结果。如下:
#*---------- Boolean algebra of return values ----------#
satisfied = (thisP(s) or thatP(s)) and (fooP(s) or barP(s))
通过之前的高级组合函数,我们可以得到如下代码:
#*------ Boolean algebra of composed functions ------#
satisfiedP = both(either(thisP,thatP), either(fooP,barP))
精简如斯啊。
一些问题下次再翻译,睡了。
转载于:https://www.cnblogs.com/zhangsong/archive/2012/07/10/2585412.html