是否可以在WebKit中检测尾部调用优化?

问题描述 投票:0回答:3

我有一个递归函数,耗尽调用堆栈是我有时遇到的问题。我知道我可以使用流、promise 和 setTimeout,但我只想编写触发尾调用优化的代码。到目前为止,似乎只有 Webkit 实现了尾部调用优化(TCO)。除了了解理论之外,还有什么方法可以检查我的代码是否会触发 TCO,无论是使用 devtools 还是通过检查 Webkit 编译器的输出?

javascript optimization webkit tail-recursion
3个回答
6
投票

我知道它并不完美,但我认为这是我们目前最好的选择。

考虑这个功能:

"use strict";

function detectTCO() {
    const outerStackLen = new Error().stack.length;
    // name of the inner function mustn't be longer than the outer!
    return (function inner() {
        const innerStackLen = new Error().stack.length;
        return innerStackLen <= outerStackLen;
    }());
}

Error.stack
不是标准的,但它在所有现代浏览器中都有基本支持。尽管如此,它在不同的浏览器中具有不同的值,当一个较长的命名函数尾部调用一个较短的命名函数时,堆栈跟踪:

  • 使用 TCO:变得更短,因为外部函数的堆栈帧被内部函数的堆栈帧替换。
  • 没有 TCO:变得更长,因为内部函数的堆栈帧被附加。

截至目前,这对于 Safari 会返回

true
,对于其他浏览器会返回
false
,这是当前的 TCO 支持。

在节点 REPL 中,

Error.stackTraceLimit
默认为 10,并且在调用
detectTCO
时已经满了。因此,当内堆叠框架被推到顶部时,底部框架被推出,因此整体长度可以变小。为了防止这种情况,可以将限制增加
Error.stackTraceLimit = 30;
或通过从
detectCTO
调用它来使用干净的堆栈运行
setTimeout

"use strict";

function detectTCO() {
    const outerStackLen = new Error().stack.length;
    // name of the inner function mustn't be longer than the outer!
    return (function inner() {
        const innerStackLen = new Error().stack.length;
        return innerStackLen <= outerStackLen;
    }());
}

document.getElementById('result').innerText = detectTCO() ? 'Yes' : 'No';
#result {
    color: blue;
    font-weight: bold;
}
Is TCO available? 
<div id="result"></div>


1
投票

可以使用

.toString()
RegExp.test()
来检查
return
语句后跟下划线、或 $ 符号、a-z 字符; 后跟左括号,后跟任意字符,后跟右括号

function forEach(arr, callback, start) {
  if (0 <= start && start < arr.length) {
    callback(arr[start], start, arr);
    return forEach(arr, callback, start + 1); // tail call
  }
}

console.log(/return [_a-z$]+\(.*\)/i.test(forEach.toString()))


0
投票

你总是可以用最愚蠢的方式来做到这一点 - 在 try、catch 块中运行带有一些“大”参数的尾递归函数。如果抛出异常,则 TCO 不起作用。

© www.soinside.com 2019 - 2024. All rights reserved.