MATLAB是否执行尾调用优化?

She*_*evy 13 matlab functional-programming tail-recursion tail-call-optimization

我最近学习了Haskell,并试图在可能的情况下将纯函数式传递给我的其他代码.这方面的一个重要方面是将所有变量视为不可变,即常量.为了做到这一点,许多将使用命令式循环实现的计算必须使用递归来执行,这通常由于为每个函数调用分配新的堆栈帧而导致存储器损失.在尾调用的特殊情况下(其中被调用函数的返回值立即返回给被调用者的调用者),然而,这种惩罚可以被称为尾调用优化的过程绕过(在一种方法中,这可以通过在正确设置堆栈后,基本上用jmp替换一个调用).MATLAB默认执行TCO,还是有办法告诉它?

Gar*_*han 11

如果我定义一个简单的尾递归函数:

function tailtest(n)
  if n==0; feature memstats; return; end
  tailtest(n-1);
end
Run Code Online (Sandbox Code Playgroud)

并调用它以便它会非常深入地进行:

set(0,'RecursionLimit',10000);
tailtest(1000);
Run Code Online (Sandbox Code Playgroud)

然后它看起来并不像堆栈帧占用了大量内存.但是,如果我让它更加深入:

set(0,'RecursionLimit',10000);
tailtest(5000);
Run Code Online (Sandbox Code Playgroud)

那么(在我的机器上,今天)MATLAB简直崩溃了:这个过程毫不客气地死了.

我不认为这与MATLAB做任何TCO一致; 函数尾部调用自身的情况,只在一个地方,除了单个参数之外没有局部变量,就像任何人都希望的那样简单.

所以:不,看起来MATLAB根本不做TCO,至少在默认情况下是这样.我(到目前为止)还没有找到可能启用它的选项.如果有的话,我会感到惊讶.

如果我们没有炸掉堆栈,递归费用是多少?请参阅我对Bill Cheatham的回答的评论:看起来时间开销是非常重要但不是疯狂.

......除非Bill Cheatham在我发表评论后删除了答案.好.因此,我采用了Fibonacci函数的简单迭代实现和一个简单的尾递归函数,在两者中进行了基本相同的计算,并将它们计时fib(60).递归实现比迭代实现的运行时间长约2.5倍.当然,对于执行比一次加法更多工作的函数和每次迭代一次减法的函数,相对开销会更小.

(我也同意delnan的观点:在Haskell中感觉很自然的那种高递归代码在MATLAB中通常可能是单一的.)

  • 在Matlab中TCO可能很难,因为从堆栈框架的工作空间清除局部变量会对onCleanup()功能产生副作用.Matlab不是GCed Java风格; 它是确定性的,使用引用计数或类似的.支持RAII.要确定堆栈帧省略是否安全,它必须深入搜索尾部调用中未传递的所有局部变量,以查看它们是否持有任何onCleanup.昂贵的测试.此外,在调用assignin(...,'caller')或evalin(...,'caller')的情况下,可能需要保留至少一个父堆栈帧.同意; unidiomatic. (3认同)