接上文,我们定义了判断某行是否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))精简如斯啊。一些问题下次再翻译,睡了。