• 通过闭包避免递归过程中的重复计算


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

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

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

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

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

  • 相关阅读:
    级联操作
    深入解析http协议
    http状态码
    数据库在一对一、一对多、多对多怎么设计表关系
    [转载]C#深拷贝的方法
    TraceSource记录程序日志
    .NET 垃圾回收与内存泄漏
    DevExpress GridControl使用方法总结
    DevExpress使用技巧总结
    Oracle 错误代码小结
  • 原文地址:https://www.cnblogs.com/xiangnan/p/3389812.html
Copyright © 2020-2023  润新知