如何使用内联递归?

How to use inline with recursion?

本文关键字:递归 何使用      更新时间:2023-10-16

来自Scott的Programming Language Pragmatics,关于内联和递归:

在一般情况下,串联扩展也不是一种选择 递归子例程。对于递归的偶尔情况 调用是可能的,但不太可能,可能需要生成一个 true 递归子例程,但要扩展该例程的一个级别 在每个呼叫站点在线

举一个简单的例子,考虑一个二叉树,其叶子包含 字符串。返回此树边缘的例程( 叶子中值的从左到右串联(可能看起来 就像C++这样的:

string fringe(bin_tree *t) {
// assume both children are nil or neither is
if (t->left == 0) return t->val;
return fringe(t->left) + fringe(t->right);
}

如果编译器使每个嵌套调用都成为真正的子例程调用,则可以内联扩展此代码。由于二进制文件中的一半节点 树是叶子,这种扩展将消除一半的动态调用 在运行时。

如果我们不仅扩展根调用,而且扩展(两个调用的一个级别( 在真正的子例程版本中调用,只有四分之一 原始动态调用将保留。

我无法理解以下句子:

  • "在每个呼叫站点内联扩展该例程的一个级别">
  • "如果此代码使每个嵌套调用成为真正的子例程调用,请以内联方式扩展此代码。">
  • "不仅扩展根调用,还扩展真正的子例程版本中的两个调用(一级(">

它们实际上意味着什么?你能用给定的例子来解释它们吗,例如,展示每个句子的操作后的代码是什么样的?

谢谢。

到目前为止,理解这一点的最简单方法是将内联视为创建替代二进制表示形式。 例如,除了创建基本string fringe(bin_tree *t),编译器还可以决定创建string fringe__inlined(bin_tree *t)。而在fringe__inlined中,实际的内联函数将是fringe的一个或两个副本。

甚至可以以这种方式创建fringe__inlined__inlined(如前所述,两个级别(。

一些编译器可以扩展和内联相当嵌套的递归调用。

例如以下代码(我没有使用inline关键字!

static int fact(int n) {
if (n<=1) return 1;
else return n* fact(n-1);
}
extern "C" int f5();
int f5() {
return fact(5);
}    

由 GCC 7.2(在 x86-64 上的 Linux/Debian/Sid 上(编译(使用g++ -O2 -fverbose-asm -S(编译成一个简单的函数,返回 120:

.text
.p2align 4,,15
.globl  f5
.type   f5, @function
f5:
.LFB1:
.cfi_startproc
# e.cc:10: };
movl    $120, %eax      #,
ret
.cfi_endproc
.LFE1:
.size   f5, .-f5
.ident  "GCC: (Debian 7.2.0-1) 7.2.0"
.section        .note.GNU-stack,"",@progbits

请注意,fact已完全内联,不会出现在生成的汇编程序代码中。

"在每个呼叫站点内联扩展该例程的一个级别">

这意味着f5将被编译成等效的

int f5() { return 5*fact(4); }

fact出现在生成的代码中,并编译为递归(机器码(函数(使用调用堆栈(。

您能否用给定的示例来解释它们,例如,显示代码是什么样的

注意:编译器/优化器不会对您编写的C++代码执行这些操作,而是对某些内部表示执行这些操作。可以用C++来演示这个想法,但由于语法和可读性问题,我们可能需要引入额外的更改,例如临时变量。

在每个呼叫站点内联扩展该例程的一个级别。

result = fringe(t);

成为

if (t->left == 0) result = t->val;
else result = fringe(t->left) + fringe(t->right);

不仅扩展根调用,还扩展真正的子例程版本中的两个调用(一级(

if (t->left == 0) result = t->val;
else {
string left, right;
// one level of expansion for left subtree
if (t->left->left == 0) left = t->left->val;
else left = fringe(t->left->left) + fringe(t->left->right);
// one level of expansion for right subtree
if (t->right->left == 0) right = t->right->val;
else right = fringe(t->right->left) + fringe(t->right->right);
result = left + right;
}