【问题标题】:Is this function recursive even though it doesn't call itself?这个函数是否是递归的,即使它不调用自己?
【发布时间】:2015-01-09 21:49:29
【问题描述】:
from pythonds.basic.stack import Stack

rStack = Stack()

def toStr(n,base):
    convertString = "0123456789ABCDEF"
    while n > 0:
        if n < base:
            rStack.push(convertString[n])
        else:
            rStack.push(convertString[n % base])
        n = n // base
    res = ""
    while not rStack.isEmpty():
        res = res + str(rStack.pop())
    return res

print(toStr(1345,2))

我指的是this tutorial 并粘贴了上面的代码。该教程说该函数是递归的,但我在任何地方都看不到递归调用,只是一个while循环。我错过了什么?

【问题讨论】:

  • 天哪,本教程的作者从未听说过 PEP-8?
  • @IanAuld 有什么问题?代码可读性很强。
  • C/C++ doThis(可能还有其他一些),Python does_this。更具可读性。 BDFL 规定了它,所以我们必须遵守 :-)。

标签: python recursion


【解决方案1】:

你说得对,这个特定的函数不是递归的。然而,上下文是,在上一张幻灯片中有一个递归函数,在这张幻灯片中,他们想展示它在内部的行为。他们后来说:

前面的例子[即有问题的 - B.] 让我们深入了解 Python 如何实现递归函数调用。

所以,是的,标题具有误导性,它应该是扩展递归函数用堆栈模仿递归函数行为或类似的东西。

有人可能会说,这个函数在某种意义上对正在解决的问题采用了递归方法/策略,但它本身并不是递归的。

【讨论】:

    【解决方案2】:

    一种递归算法,顾名思义,is a method where the solution to a problem depends on solutions to smaller instances of the same problem

    这里的问题是将数字转换为给定符号的字符串。

    函数执行的数据“存储”实际上如下所示:

    push(d1)
    push(d2)
    ...
    push(dn-1)
    push(dn)
    
    res+=pop(dn)
    res+=pop(dn-1)
    ...
    res+=pop(d2)
    res+=pop(d1)
    

    实际上是:

    def pushpop():
        push(dx)
        pushpop(dx+1...dn)
        res+=pop(dx)
    

    处理特定数据块的步骤包含处理其余数据的所有步骤(每个块都以相同的方式处理)。

    如果 function 是递归的(因为他们倾向于将这个术语应用于狭义的子例程),可以争论,但它实现的 algorithm 绝对是。


    为了让您更好地感受差异,以下是针对同一问题的迭代解决方案:

    def toStr(n,base):
        charmap = "0123456789ABCDEF"
        res=''
        while n > 0:
            res = charmap[n % base] + res
            n = n // base
        return res
    

    如您所见,此方法的内存占用要低得多,因为它不储存 任务。这就是区别:迭代算法使用相同的状态实例执行每个步骤,方法是改变它,而递归算法为每个步骤创建一个新实例,如果仍然需要旧的,则必须储存它们。

    【讨论】:

    • 用实现 TCO 的语言编写的尾递归函数是否不再递归?毕竟,它的足迹不再与深度成正比。
    • TCO 只是内部优化。展开的循环仍然是循环吗?无论如何,你有没有其他方法来区分迭代和递归之间的区别?
    • 作为一个在 Haskell 附近有一段时间的人,我很想将函数视为纯粹的数学构造,将它们的定义的属性与那些任何相关的运行时实现。
    • 我想我明白了——区分的方法。
    【解决方案3】:

    因为您使用的是堆栈结构。

    如果您考虑如何实现函数调用,递归本质上是一种让编译器为您管理调用堆栈的简单方法。

    这个函数手动完成所有的堆栈处理,但它在概念上仍然是一个递归函数,只是堆栈管理是手动完成的,而不是让编译器来做。

    【讨论】:

    • 使用堆栈不会使函数递归。堆栈只是大多数(如果不是全部)语言用来处理嵌套函数调用的数据结构,其中递归调用只是一种类型。
    • 我不会马上驳回这个答案。根据递归的定义,它是“将规则、定义或过程重复应用于连续的结果”。它没有提到需要将自己称为递归。堆栈结构似乎是帮助概念化递归概念的好方法,而无需函数调用自身。我错了吗?
    • @kalin,堆栈结构是实际实现递归的方式——看看调用堆栈(递归所依赖的)是如何实现的。所以这不仅仅是一个有用的概念,它也是真实事物的实现方式。
    • @kalin 这不是“递归”的一个很好的定义,因为该定义可以很容易地应用于“迭代”,并且没有捕捉到“自引用”的通常概念,即“递归”意味着...
    • @kalin,好吧,在一定程度上,反对意见是正确的——使用堆栈(或等效的)是必要的,但对于传统的递归函数实现来说还不够。就该反对意见是否使其成为错误答案而言,这是一种平衡行为;个人认为不是。话虽如此,我会(仅)同意反对意见,因为采用“递归”的定义,允许不称自己的函数被视为显式递归,而不是演示递归,将剥夺单词的某些理解含义。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-06
    • 1970-01-01
    • 2016-04-05
    • 1970-01-01
    • 2016-08-12
    • 2017-04-19
    相关资源
    最近更新 更多