简体   繁体   English

使用递归获取Python中的列表长度

[英]Get length of list in Python using recursion

I am trying to calculate the length of a list. 我正在尝试计算列表的长度。 When I run it on cmd, I get: 当我在cmd上运行它时,我得到:

RuntimeError: maximum recursion depth exceeded in comparison

I don't think there's anything wrong with my code: 我认为我的代码没有任何问题:

def len_recursive(list):
    if list == []:
        return 0
    else:
        return 1 + len_recursive(list[1:])

Don't use recursion unless you can predict that it is not too deep. 除非你能预测它不是太深,否则不要使用递归。 Python has quite small limit on recursion depth. Python对递归深度的限制非常小。

If you insist on recursion, the efficient way is: 如果你坚持递归,有效的方法是:

def len_recursive(lst):
    if not lst:
        return 0
    return 1 + len_recursive(lst[1::2]) + len_recursive(lst[2::2])

The recursion depth in Python is limited, but can be increased as shown in this post . 在Python递归深度是有限的,但如本可以增加 If Python had support for the tail call optimization, this solution would work for arbitrary-length lists: 如果Python支持尾调用优化,则此解决方案适用于任意长度列表:

def len_recursive(lst):
    def loop(lst, acc):
        if not lst:
            return acc
        return loop(lst[1:], acc + 1)
    return loop(lst, 0)

But as it is, you will have to use shorter lists and/or increase the maximum recursion depth allowed. 但事实上,您将不得不使用更短的列表和/或增加允许的最大递归深度。

Of course, no one would use this implementation in real life (instead using the len() built-in function), I'm guessing this is an academic example of recursion, but even so the best approach here would be to use iteration, as shown in @poke's answer. 当然,没有人会在现实生活中使用这个实现(而是使用len()内置函数),我猜这是一个递归的学术例子,但即便如此,这里最好的方法是使用迭代,如@ poke的回答所示。

As others have explained, there are two problems with your function: 正如其他人所解释的那样,您的功能存在两个问题:

  1. It's not tail-recursive, so it can only handle lists as long as sys.getrecursionlimit . 它不是尾递归的,因此它只能处理列表,只要sys.getrecursionlimit
  2. Even if it were tail-recursive, Python doesn't do tail recursion optimization. 即使它是尾递归的,Python也不会进行尾递归优化。

The first is easy to solve. 第一个很容易解决。 For example, see Óscar López's answer. 例如,请参阅ÓscarLópez的回答。

The second is hard to solve, but not impossible. 第二个很难解决,但并非不可能。 One approach is to use coroutines (built on generators) instead of subroutines. 一种方法是使用协同程序(基于生成器)而不是子程序。 Another is to not actually call the function recursively, but instead return a function with the recursive result, and use a driver that applies the results. 另一种方法是不实际以递归方式调用函数,而是返回带递归结果的函数,并使用应用结果的驱动程序。 See Tail Recursion in Python by Paul Butler for an example of how to implement the latter, but here's what it would look like in your case. 请参阅Paul Butler的Python中Tail Recursion ,了解如何实现后者的示例,但这就是您的情况。

Start with Paul Butler's tail_rec function: 从Paul Butler的tail_rec函数开始:

def tail_rec(fun):
    def tail(fun):
        a = fun
        while callable(a):
            a = a()
        return a
    return (lambda x: tail(fun(x)))

This doesn't work as a decorator for his case, because he has two mutually-recursive functions. 这不适合作为他的案例的装饰者,因为他有两个相互递归的函数。 But in your case, that's not an issue. 但在你的情况下,这不是问题。 So, using Óscar López's's version: 所以,使用ÓscarLópez的版本:

@tail_rec
def tail_len(lst):
    def loop(lst, acc):
        if not lst:
            return acc
        return lambda: loop(lst[1:], acc + 1)
    return lambda: loop(lst, 0)

And now: 现在:

>>> print tail_len(range(10000))
10000

Tada. 田田。

If you actually wanted to use this, you might want to make tail_rec into a nicer decorator: 如果您真的想要使用它,您可能希望将tail_rec变成更好的装饰器:

def tail_rec(fun):
    def tail(fun):
        a = fun
        while callable(a):
            a = a()
        return a
    return functools.update_wrapper(lambda x: tail(fun(x)), fun)

Your exception message means that your method is called recursively too often, so it's likely that your list is just too long to count the elements recursively like that. 您的异常消息意味着您的方法过于频繁地被递归调用,因此您的列表可能太长,无法像这样递归地计算元素。 You could do it simply using a iterative solution though: 你可以简单地使用迭代解决方案:

def len_iterative(lst):
    length = 0
    while lst:
        length += 1
        lst = lst[1:]
    return length

Note that this will very likely still be a terrible solution as lst[1:] will keep creating copies of the list. 请注意,这很可能仍然是一个糟糕的解决方案,因为lst[1:]将继续创建列表的副本。 So you will end up with len(lst) + 1 list instances (with lengths 0 to len(lst) ). 所以你最终会得到len(lst) + 1列表实例(长度为0len(lst) )。 It is probably the best idea to just use the built-in len directly, but I guess it was an assignment. 直接使用内置len可能是最好的主意,但我想这是一个任务。

Imagine you're running this using a stack of paper. 想象一下,你正在使用一叠纸运行它。 You want to count how many sheets you have. 你想要计算你有多少张。 If someone gives you 10 sheets you take the first sheet, put it down on the table and grab the next sheet, placing it next to the first sheet. 如果有人给你10张纸,你拿第一张纸,把它放在桌子上,抓住下一张纸,放在第一张纸旁边。 You do this 10 times and your desk is pretty full, but you've set out each sheet. 你这样做10次,你的桌子很满,但你已经列出了每张纸。 You then start to count every page, recycling it as you count it up, 0 + 1 + 1 + ... => 10. This isn't the best way to count pages, but it mirrors the recursive approach and python's implementaion. 然后你开始计算每一页,在计算它时回收它,0 + 1 + 1 + ... => 10.这不是计算页面的最佳方法,但它反映了递归方法和python的实现。

This works for small numbers of pages. 这适用于少量页面。 Now imagine someone gives you 10000 sheets. 现在想象有人给你10000张。 Pretty soon there is no room on your desk to set out each page. 很快你桌子上就没有空间来展示每一页。 This is essentially what the error message is telling you. 这基本上是错误消息告诉您的内容。

The Maximum Recursion depth is "how many sheets" can the table hold. 最大递归深度是表可以容纳的“多少张”。 Each time you call python needs to keep the "1 + result of recursive call" around so that when all the pages have been laid out it can come back and count them up. 每次调用python时都需要保持“1 +递归调用结果”,这样当所有页面都已布局时,它可以返回并计算它们。 Unfortunately you're running out of space before the final counting-up occurs. 不幸的是,在最后的计数发生之前,你的空间已经用完了。

If you want to do this recursively to learn, since you're want to use len() in any reasonable situation, just use small lists, 25 should be fine. 如果你想以递归的方式学习,因为你想在任何合理的情况下使用len(),只需使用小列表,25应该没问题。

Some systems could handle this for large lists if they support tail call s 如果某些系统支持尾调用,则可以为大型列表处理此问题

Python isn't optimising tail recursion calls, so using such recursive algorythms isn't a good idea. Python没有优化尾递归调用,所以使用这样的递归算法并不是一个好主意。 You can tweak stack with sys.setrecursionlimit() , but it's still not a good idea. 您可以使用sys.setrecursionlimit()调整堆栈,但它仍然不是一个好主意。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM