假设你需要递归地使用某个函数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函数包装).

 

当然,实际工作中我们不可能会如此蛋疼地转化一个字符串,此处只是为了方便说明.

因为实际工作中的情况会更加复杂,有时候可能会不知不觉地重复计算了某些东西.

反正,关键是养成这种意识,习惯成自然:

感觉递归过程出现了重复的东西?闭包之!