【问题标题】:Can every recursion be converted to trees? [closed]每个递归都可以转换为树吗? [关闭]
【发布时间】:2018-02-02 22:56:03
【问题描述】:

我想知道是否有任何正式的方式来支持这一点。老实说,我不确定这是一个好问题,但我问的目的是想知道我们是否可以在考虑递归的同时始终使用树作为心理模型。

为了更好地解释,我想知道“是否每个递归公式都对应于树遍历?”。另外,在 Dirk 的回答中查看我的 cmets

建议的重复问题没有回答我的问题。我的问题与迭代无关。

【问题讨论】:

  • ...嗯?树作为递归的心理模型?不知道你在这里问什么。世界上几乎所有的编程语言都以某种方式归结为一棵树。甚至那些不支持递归的。
  • @cHao 已编辑。现在清楚了吗?
  • @abjoshi:不是真的。 字面上的一切都可以对应于树的遍历,对于“树”和“遍历”的某些值。同样,人们可以提出它不能提出的定义。也许您应该尝试提出您所询问的这种映射的示例——这将有助于定义“树遍历”。
  • @cHao 这个怎么样。给定任何递归公式,该公式的评估是否等同于树遍历?例如,给定斐波那契递归公式,您可以将扩展表示为一棵树,并将评估表示为该树的后序遍历。那么,我的问题是,是否可以为 every 递归执行此操作?

标签: recursion tree


【解决方案1】:

在解释递归时,树遍历是一个典型的例子。但是,我认为并非每个递归都对应于树遍历:

在某些语言(lisp 是其中之一)中,递归用于实现迭代。在那里,你还必须用递归来表达无限循环,我认为这不符合你的“树作为递归的心理模型”的概念。 (如果你想知道如何使用递归实现无限循环而不遇到堆栈溢出:有一种称为尾调用消除的机制可以解决这个问题。)

【讨论】:

  • Scheme 是更好的例子,因为它实际上没有控制流,而不仅仅是用于递归的语法糖。另一方面,CL 不保证 TCO。
  • “我认为不是每个递归都对应于树遍历”。这个论点怎么样?假设在递归中总共有 n 个调用。我们用一个节点来表示每个 callee fn。如果 a 调用 b,则两个节点“a”和“b”之间有一条边。然后,图中正好有 n-1 个边(因为 n-1 个被调用者需要 n-1 个调用),这意味着它是一棵树。这是一种合理的思维方式吗?
【解决方案2】:

以下是对整数进行操作的递归函数,非树状输入

const add = (x, y) =>
  x + y

const sumTo = (x = 0) =>
  x === 0
    ? x
    : add (x, sumTo (x - 1))

console.log (sumTo ())  // 0
console.log (sumTo (1)) // 1
console.log (sumTo (2)) // 3
console.log (sumTo (4)) // 10

然而,它演变出一个树状(递归)计算sumTo (4)...

add ( 4
    , add ( 3
          , add ( 2
                , add ( 1
                      , 0
                      )
                )
         )
   )
// => 10

不过,尾递归版本具有不同的计算过程。这是 Dirk Herrmann 所做的相同区分(此处的另一个答案)

const add = (x, y) =>
  x + y

const sumTo = (x = 0, acc = 0) =>
  x === 0
    ? acc
    : sumTo (x - 1, acc + x)

console.log (sumTo ())  // 0
console.log (sumTo (1)) // 1
console.log (sumTo (2)) // 3
console.log (sumTo (4)) // 10

在这种情况下,当尾调用消除可用时,sumTo (4) 会演化出一个迭代计算

sumTo (3, 4)
sumTo (2, 7)
sumTo (1, 9)
sumTo (0, 10)
// => 10

所以回答你的问题,不。递归过程(以自己的名称引用自身的函数)可能会或可能不会演变出递归进程

这个话题在Structure and Interpretation of Computer Programs,第1.2.1节中讨论

【讨论】:

  • 我正试图解决这个问题。同时,您能否看看我对 Dirk Herrmann 回答的评论,让我知道这对您是否有意义?另外,我已经编辑了问题详细信息以使其更清晰(希望如此)
  • 我链接的阅读应该对你有帮助
  • 我想我明白你在说什么。在您的第二个示例(尾调用消除)中,对 sumTo (3, 4) 的第一次调用不等待 sumTo (2, 7) 完成。我猜第二个调用可以替换堆栈中的第一个,依此类推。呼叫转换为跳转。那么被调用者返回调用者并且没有树遍历的“通常”意义上的递归不是递归的吗?你的例子就是这个意思吗?
  • 嗯,你的“通常感觉”只是需要一些调整,但是是的,你看到了微妙但重要的区别。
  • 因示例、有用的链接和后续讨论而被接受
猜你喜欢
  • 2021-05-13
  • 2011-10-29
  • 1970-01-01
  • 2021-07-27
  • 2013-09-10
  • 2017-06-04
  • 2011-06-14
  • 1970-01-01
相关资源
最近更新 更多