如何使用内联递归?
How to use inline with recursion?
来自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;
}
- 使用递归的数组的最小值.这是怎么回事
- 使用递归时获取变量的奇怪值
- 如何使用递归打印修改后的星号三角形图案
- 使用递归模板动态分配的多维数组
- 字符串化递归的"std::vector<std::vector<...>>"而不使用部分模板函数专用化
- 为什么在递归中使用循环会产生意想不到的结果?
- 如果我们通过引用传递变量,则递归中使用的堆栈空间量是否为零?
- 有没有办法在递归中使用指针来查找数组的最小值?
- 递归如何使用堆栈数据结构
- 如何在递归中使用返回函数
- 如何使用递归函数使用 4 个字符生成 m 长度字符串数组
- c++ 递归树 使用指针和状态机构建树
- 在 Java 中有不同的结果,C++在递归中使用 +=
- 在C++的递归中使用数组
- 基例如何影响使用递归函数的哪些行
- 递归中使用一维数组
- 在递归中使用引用传递
- 递归地使用二进制搜索溢出 C++
- 分段故障中的递归函数.使用std::stack (c++)
- 需要帮助递归地使用Boost属性树库和XML创建目录树