【问题标题】:Is tail call optimization applicable to this function?尾调用优化是否适用于此功能?
【发布时间】:2018-08-30 00:58:55
【问题描述】:

如果 bar 调用 bar(i/2),如果 i 是偶数,否则 bar(3*i + 1),递归函数 bar 将进行尾递归。

const int bar(const int i) 
{
  if (i < 2) return i;
  return i % 2 ? bar(i/2) : bar(3*i + 1);
}

但是,如果 bar 调用 bar 或 foo,而后者的局部变量集与 bar 完全不同,该怎么办?

const int bar(const int i) 
{
  if (i < 2) return i;
  return i % 2 ? bar(i/2) : foo(3*i + 1);
  // where foo is very complicated recursive call that has 
  // 11 different user-defined/primitive type of
  // local variables that can't be optimized out
}

我的理解是尾递归优化将使用调用者的堆栈。调用者在调用被调用者之前已经完成了它的局部变量。因此,被调用者可以重用它。当调用者和被调用者是同一个函数时(例如 foo 调用 foo,bar 调用 bar),我的理解听起来不错。但是,如果堆栈大小和布局完全不同,并且被调用者可能是具有不同堆栈布局的多个不同函数之一,会发生什么?

首先,它会是尾递归吗? (现在,我知道可能会应用尾部“调用”优化,但不是这不是尾部“递归”。)其次,主要的编译器,如 gcc、clang 等,会优化这种尾部调用吗? (答案似乎是的。)

如果被调用者(第二个代码示例中的 foo)要复杂得多怎么办?如果被调用者和调用者相互调用(相互递归),我的第二个代码示例中的调用是否会进行尾调用优化?如果被调用者(例如 foo)是一个复杂的递归调用,绝对不是尾递归,并且编译器很难将其减少为循环左右,它是否仍然是尾调用优化的?

【问题讨论】:

  • 无论 foo() 做什么都对 bar() 没有影响。如果编译器可以优化 bar() 以进行尾递归,则它(至少在您的场景中)不必担心 foo()。假设您调用了 printf() - 这会阻止尾递归优化吗?
  • bar() 调用foo() 不是递归。所以我看不到相关性。
  • 你说的优化是“尾调用优化”。尽管它在(相互)递归中最有用,但绝不限于这种情况。
  • @CraigYoung 了解mutual recursion
  • @naomik 该问题未能在其“最小示例”中演示相互递归。 (关于foo() 的虚张声势的虚张声势的阐述并不能代替演示。)那么:你的意思是什么? OP 没有用foo() 演示任何类型的递归。所以我仍然没有看到相关性。

标签: c++ c recursion tail-recursion


【解决方案1】:

尾递归优化是否适用于该函数?

是的,尾递归优化适用于您的示例。请查看汇编程序https://godbolt.org/g/cSpUZw 获取第二个示例。应用了更渐进的优化。递归替换为循环。

bar(int):
  cmp edi, 1
  jg .L12
  jmp .L6
.L15:
  sar edi
  cmp edi, 1
  je .L14
.L12:
  test dil, 1
  jne .L15
  lea edi, [rdi+1+rdi*2]
  jmp foo(int)
.L14:
  mov eax, 1
  ret
.L6:
  mov eax, edi
  ret

【讨论】:

    【解决方案2】:

    术语“尾递归”是调用站点的本地属性。它完全不受同一方法中的其他调用的影响。

    粗略地说,如果在调用的返回和封闭方法的返回之间不需要运行可执行代码,则调用是尾递归的。

    因此,在您的示例中对bar() 的所有调用都是尾递归的。

    但请注意,如果你说

    return i % 2 ? bar(i/2) : 1 + bar(3*i + 1);
    

    那么第一个调用是尾递归的,但第二个不是因为加法1 +必须在返回后执行。

    【讨论】:

      猜你喜欢
      • 2021-05-27
      • 1970-01-01
      • 2014-06-09
      • 1970-01-01
      • 2020-10-21
      • 2010-10-23
      • 2012-04-18
      • 2011-07-16
      • 1970-01-01
      相关资源
      最近更新 更多