python 递归

python 递归

递归算是一直对我来说比较致命的一点,一直没有办法非常通透的理解。最近在业务上有一段代码几乎想不到非递归的场景。所以在这次加深了对递归的认识。

python 递归的限制

首先递归第一个要考虑的就是什么时候结束。结果条件没写好就会导致调用栈太深,甚至爆栈的危险。python在这里做了最大递归层数的限制。

sys.getrecursionlimit()
一般设置为1000。 当然如果你非常有信息可以把值设置得更大一些。通过相应的set 方法。但是这只是对层数的限制,相应的栈大小没有变。还是可能会爆栈,所以还是应该谨慎使用。(目前认为 栈的限制在于对线程栈大小限制不区分语言 ulimit 可以查看).

深度优先搜索

关于深度优先搜索,对于树而言他的递归其实是可以预知的,那就是树的深度。(当然,前提是你知道树的深度)在这种情况下基本不用考虑爆栈的危险。为什么使用深度递归搜索呢?因为某些时候把递归转为迭代太耗费脑子了。

如这一段代码,意义在于同步json和DB之间的数据。如果json dict有不同则新插入数据库。我们会发现在传入到新的函数是会新的值,也就是有状态的转移。而对于非递归的时候几乎只能够说是遍历,而无法很好的修改参数以及恢复参数。

def location_dfs(tree_node, node_name, parentid=0, level=0):

	location = get_location(name=node_name, parentid=parentid, level=level)

	if not location:
		location = add_location(name=node_name, parentid=parentid, level=level)
		log.data('regular_sync_location_tab| new location info: id:%s parentid:%s name:%s level:%s',
				 location.id, location.parentid, location.name, location.level)
		print 'add location id:%s name:%s parentid:%s' % (location.id, location.name, location.parentid)
		add_notify_list(location)

	for key in tree_node:
		if isinstance(tree_node, dict):
			next_subtree = tree_node[key]
		else:
			next_subtree = {}
		location_dfs(next_subtree, key, parentid=location.id, level=level + 1)

一开始想看到最末尾也是调用其自身,让我想起了尾递归优化,仔细一看使其不然,代码是在for循环是调用前自身,所以不是尾递归。尾递归应该可以改为return xxx的形式。

尾递归优化

尾递归优化定义:尾调用的概念非常简单,一句话就能说清楚,就是指某个函数的最后一步是调用另一个函数。
一下不属于尾递归


// 情况一
function f(x){
  let y = g(x);
  return y;
}

// 情况二
function f(x){
  return g(x) + 1;
}

以下属于尾递归

function factorial (num, total) {
    if (num === 1) return total;
    return factorial(num - 1, num * total);
}

针对这种情景很多编译器会进行优化。python 编译器没有优化功能,但是有一个装饰器可以实现这个功能,也能让我们更清楚尾递归到底是如何优化的。

import sys

class TailCallException:
    def __init__(self, args, kwargs):
        self.args = args
        self.kwargs = kwargs

def tail_call_optimized(func):
    def _wrapper(*args, **kwargs):
        f = sys._getframe()
        if f.f_back and f.f_back.f_back and f.f_code == f.f_back.f_back.f_code:
            raise TailCallException(args, kwargs)

        else:
            while True:
                try:
                    return func(*args, **kwargs)
                except TailCallException, e:
                    args = e.args
                    kwargs = e.kwargs
    return _wrapper

@tail_call_optimized
def fib(n, a, b):
    if n == 1:
        return a
    else:
        return fib(n-1, b, a+b)

r = fib(1200, 0, 1) #突破了调用栈的深度限制

其核心思想就是:调用栈函数内容是一致的。当检查到3个一样的栈时,就会抛出异常,把参数带上,被上一层的fib捕获,这时候栈已经收缩。while True 内部继续调用,程序继续运行。最终的效果是递归栈的深度不超过3.

参考:尾递归优化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值