通过闭包避免递归过程中的重复计算
假设你需要递归地使用某个函数func,你给它4个参数:
fixed1,fixed2,fixed3,arg1
其中,fixed1,fixed2,fixed3表示在递归过程中固定不变的参数.(你可能还需要对它们进行一些处理,返回另外一些固定的数据,再一起加入到函数返回值中.)
arg1是变动参数(它控制函数向递归的终点靠近).
比如,出于某种奇特的念头,我想用递归的方法将某个字符串拆分成有特定前缀的列表:
def func(fixed1,fixed2,fixed3,arg1): if arg1!='': yield '%s%s%s->%s'%(fixed1,fixed2,fixed3,arg1[0]) for i in func(fixed1,fixed2,fixed3,arg1[1:]): yield i print list(func('a','b','c','helloworld!'))
结果:
['abc->h', 'abc->e', 'abc->l', 'abc->l', 'abc->o', 'abc->w', 'abc->o', 'abc->r', 'abc->l', 'abc->d', 'abc->!']
能达到目的,但是每次递归的时候都传入了3个固定不变的参数,浪费了.
而且,如果你需要对固定参数预先进行某种处理,返回一些固定的数据,那么这种浪费就更严重了,因为每次递归你都要再次处理这些固定参数,得到完全相同的数据.
比如说上面那个例子换一种形式:
def func(fixed1,fixed2,fixed3,arg1): pre=''.join([fixed1,fixed2,fixed3]) if arg1!='': yield '%s->%s'%(pre,arg1[0]) for i in func(fixed1,fixed2,fixed3,arg1[1:]): yield i
第二行代码就相当于我说的"对固定参数预先进行某种处理,返回一些固定的数据"
我们可以通过闭包来解决这种浪费:
def func(fixed1,fixed2,fixed3,arg1): pre=''.join([fixed1,fixed2,fixed3]) def inf(arg1): if arg1!='': yield '%s->%s'%(pre,arg1[0]) for i in inf(arg1[1:]): yield i return list(inf(arg1))
可以看到,func函数内部的生成器inf只接受arg1这个变动的参数,pre作为inf的自由变量,只需在func中计算一次就能反复被inf的递归过程调用.绿色低碳.
另外,由于采用闭包的形式,func返回结果的形式也更加灵活了.我可以直接返回inf(arg1),也可以对它进行某种包装再返回(如例子中用list函数包装).
当然,实际工作中我们不可能会如此蛋疼地转化一个字符串,此处只是为了方便说明.
因为实际工作中的情况会更加复杂,有时候可能会不知不觉地重复计算了某些东西.
反正,关键是养成这种意识,习惯成自然:
感觉递归过程出现了重复的东西?闭包之!