我有一个简单的程序,用JavaScript定义Ackermann函数:
function ack(m,n,p){
if(p==0) return m+n;
if(n==0&&p==1) return 0;
if(n==0&&p==2) return 1;
if(n==0&&p>2) return m;
return ack(m,ack(m,n-1,p),p-1)
}
由here定义,用于评估tetration和更高扩展名。这适用于添加整数,如:
ack(m,n,0);
乘:
ack(m,n,1);
Expontiation:
ack(m,n,2);
并且,Tetration:
ack(m,n,3);
此尝试因值m, n > 2
而失败,抛出:InternalError: too much recursion
。我知道这通常发生在非终止递归函数(如var inf_rec = x => inf_rec(x)
),但此函数确实终止。
有没有办法绕过InternalError?
我应该怎么做,因为我显然需要更深的细胞堆叠?
答案 0 :(得分:2)
否这取决于为每个浏览器设置的最大堆栈大小。有一个链接here,其中包含每个浏览器的所有相关堆栈最大大小的答案,以及在该特定浏览器中查看堆栈的方法。
答案 1 :(得分:2)
我以非递归方式重写了您自己的实现,现在它已经完成了ack(2, 3, 3);
,这是65536
,但它似乎永远不会达到{{1}之类的计算结束}。我没有得到任何堆栈溢出错误,即使等待至少5分钟计算结束......这看起来很可疑。
以下是实施:
ack(3, 3, 3);

var logEl = document.getElementById('log');
log('ack(1, 2, 0)', ack(1, 2, 0));
log('ack(8, 2, 1)', ack(8, 2, 1));
log('ack(4, 2, 2)', ack(4, 2, 2));
log('ack(2, 3, 3)', ack(2, 3, 3));
//ack(3, 3, 3) never seem to end, but I did not get a stack overflow error...
function ack(m, n, p) {
var callStack = [[m, n, p]],
valueStack = [],
item;
while (item = callStack.pop()) {
m = item[0];
n = item[1] !== null? item[1] : valueStack.pop();
p = item[2];
if (p === 0) { valueStack.push(m+n); continue; }
if (n === 0 && p === 1) { valueStack.push(0); continue; }
if (n === 0 && p === 2) { valueStack.push(1); continue; }
if (n === 0 && p > 2) { valueStack.push(m); continue; }
callStack.push([m, null, p - 1]);
callStack.push([m, n - 1, p]);
}
return valueStack.pop();
}
function log(exp, val) {
var li = document.createElement('li');
li.textContent = exp + ' -> ' + val + '\n';
logEl.appendChild(li);
}

答案 2 :(得分:0)
该错误与非终止与终止调用堆栈无关。相反,它与调用堆栈的深度有关。根据浏览器的实现,可以递归地进行多少函数调用是有限制的。绕过此问题的唯一方法是确保您的调用堆栈永远不会达到该限制。