阅读了Rauschmayer博士对es6中递归尾调用优化的描述之后,我一直试图重新创建他详细说明的递归因子函数的"零堆栈"执行.
使用Chrome调试器在堆栈帧之间切换,我发现尾部优化没有发生,并且正在为每次递归创建堆栈帧.
我也尝试通过在没有调试器的情况下调用函数来测试优化,而是传递100000给阶乘函数.这会引发"最大堆栈"错误,这意味着它实际上并未进行优化.
这是我的代码:
const factorial = (n, acc = 1) => n <= 1 ? acc : factorial(n - 1, n * acc)
console.log( factorial(100000) )
Run Code Online (Sandbox Code Playgroud)
结果:
Uncaught RangeError: Maximum call stack size exceeded
Run Code Online (Sandbox Code Playgroud) javascript stack-overflow optimization recursion ecmascript-6
有人可以解释一下,为什么我们在JavaScript中使用回调?我找到了示例,但它们可以通过使用普通函数来实现.使用它有什么好处?我得到了"如何"使用它的答案,而不是"为什么和何时"我们需要使用它.
通常,我发现它在AJAX中使用.在...上httpRequest.onreadystatechange.这类似于Java的多线程吗?响应的听众如何以及在哪里?异步编程是否类似于多线程?
在以下代码中,控制流程如何:
function some_function(arg1, arg2, callback) {
var my_number = Math.ceil(Math.random() * (arg1 - arg2) + arg2);
callback(my_number);
some_different_function_not_callback(arg1);
}
some_function(5, 15, function(num) {
console.log("callback called! " + num);
});
Run Code Online (Sandbox Code Playgroud)
来自JQuery网站:
关于回调的特殊之处在于,在"父"之后出现的函数可以在回调执行之前执行"(参考:http://docs.jquery.com/Tutorials:How_jQuery_Works)
有人可以用一个例子向我解释这一行吗?
据我所知,递归非常优雅,但在OOP和程序编程方面效率不高(参见精彩的"High Order perl",Mark Jason Dominus).我有一些信息,在函数式编程递归中很快 - 保持其优雅和简洁.有人可以确认并可能放大这个吗?我正在考虑XSLT和Haskell(我的下一个语言学习列表的高位)
谢谢
丹尼尔
xslt optimization haskell functional-programming tail-recursion
根据我的经验,有许多代码明确使用内联函数,这需要权衡:
问题是:链接时优化(例如,在GCC中)是否呈现手动内联,例如,在C99中声明一个"内联"函数并提供一个实现,已经过时了?我们是否真的不需要考虑自己内联大多数函数?那些总是从内联中受益的函数呢,例如deg_to_rad(x)?
澄清:我不是在考虑同一个翻译单元中的函数,而是考虑逻辑上应该存在于不同翻译单元中的函数.
更新:我经常看到反对"内联",并建议过时.但是,就个人而言,我确实经常看到明确的内联函数:作为类体中定义的函数.
当我用g ++编译下面的简单递归代码时,汇编代码只返回i,好像g ++可以像人类那样做一些代数技巧.
int Identity(int i) {
if (i == 1)
return 1;
else
return Identity(i-1)+1;
}
Run Code Online (Sandbox Code Playgroud)
我不认为这种优化是关于尾递归的,显然,g ++必须至少做这两件事:
如何重现
% g++ -v
gcc version 8.2.1 20181127 (GCC)
% g++ a.cpp -c -O2 && objdump -d a.o
Disassembly of section .text:
0000000000000000 <_Z8Identityi>:
0: 89 f8 mov %edi,%eax
2: c3
Run Code Online (Sandbox Code Playgroud)
更新: 感谢很多人回答这个问题.我在这里收集了一些讨论和更新.
更新+回答: 感谢下面的答案(我已经标记它有用,并且还检查了manlio的答案),我想我理解编译器如何以简单的方式完成此操作.请参阅下面的示例.首先,现代gcc可以做一些比尾递归更强大的东西,所以代码转换成这样的东西:
// Equivalent to return i
int Identity_v2(int i) {
int ans = 0;
for (int …Run Code Online (Sandbox Code Playgroud) MATLAB是一种按值传递的语言.我有一个处理像素邻居的递归函数.每次调用函数时,复制图像(在我的情况下是两个图像)是非常昂贵的.
我使用全局变量来解决问题.有没有其他方法可以使递归函数修改数组?
我正在通过"现在学习Prolog"在线书籍来获取乐趣.
我正在尝试编写一个谓词,该谓词遍历列表的每个成员并使用累加器向其中添加一个谓词.我已经很容易做到了,没有尾递归.
addone([],[]).
addone([X|Xs],[Y|Ys]) :- Y is X+1, addone(Xs,Ys).
Run Code Online (Sandbox Code Playgroud)
但我已经读过,出于性能原因,最好避免这种类型的递归.这是真的?总是使用尾递归被认为是"好习惯"吗?是否值得努力使用累加器来养成良好的习惯?
我试图将此示例更改为使用累加器,但它会反转列表.我怎么能避免这个?
accAddOne([X|Xs],Acc,Result) :- Xnew is X+1, accAddOne(Xs,[Xnew|Acc],Result).
accAddOne([],A,A).
addone(List,Result) :- accAddOne(List,[],Result).
Run Code Online (Sandbox Code Playgroud) 给定具有无限递归的C程序:
int main() {
main();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
为什么会导致堆栈溢出.我知道这导致C++中来自以下线程的未定义行为这是无限递归UB吗?(并且作为无法main()在C++中调用的副节点).但是,valgrind告诉我这会导致堆栈溢出:
Stack overflow in thread 1: can't grow stack to 0x7fe801ff8
Run Code Online (Sandbox Code Playgroud)
最后由于分段错误,程序结束:
==2907== Process terminating with default action of signal 11 (SIGSEGV)
==2907== Access not within mapped region at address 0x7FE801FF0
Run Code Online (Sandbox Code Playgroud)
这是C中的未定义行为,还是应该导致堆栈溢出,为什么会导致堆栈溢出?
编辑
1我想知道C中是否允许无限递归?
2这是否会导致堆栈溢出?(已经得到了充分的回答)
我使用和阅读有关@tailrec注释的尾部递归方法.我已经通过许多链接解释了它.例如,它仅适用于自调用函数,不应覆盖等.
它到处都提到了compiler optimizes.但是编译器做了什么魔术/概念使其尾递归.对于下面的简单函数,编译器会做什么:
@tailrec def fact(acc: Int, n: Int): Int = {
if (n <= 1) acc
else fact(n * acc, n - 1)
}
fact(1,10)
Run Code Online (Sandbox Code Playgroud)
我的意思是它将它转换为循环,重复调用它然后返回最终值?是否有纸张的链接解释它
optimization ×4
recursion ×3
c ×2
c++ ×2
gcc ×2
haskell ×2
javascript ×2
accumulator ×1
ecmascript-6 ×1
g++ ×1
ghc ×1
inline ×1
jquery ×1
matlab ×1
prolog ×1
scala ×1
xslt ×1