如何获得打字稿来执行尾部递归优化?
const isPositive = (n: number) => n > 0;
function fitsIn(dividend: number,
divisor: number,
count: number,
accum: number): number {
if (accum + divisor > dividend) {
return count;
}
return fitsIn(dividend, divisor, count + 1, accum + divisor);
}
function divide(dividend: number, divisor: number): number {
let timesFits = fitsIn(Math.abs(dividend), Math.abs(divisor), 0, 0);
return isPositive(dividend) === isPositive(divisor) ? timesFits : -timesFits;
}
console.log(divide(10, 3));
// 3
console.log(divide(-2147483648, -1));
// RangeError: Maximum call stack size exceeded
console.log(divide(10000, 1));
// RangeError: Maximum call stack size exceeded
我尝试在严格模式下使用Typescript 4.6.2运行此代码,这导致堆栈溢出。递归调用是在功能的末尾,积累是在递归功能调用中完成的。是否应该为尾部递归优化此代码吗?应该更改什么以使尾随递归优化发生?
const isPositive = (n: number) => n > 0;
function fitsIn(dividend: number,
divisor: number,
count: number,
accum: number): number {
if (accum + divisor > dividend) {
return count;
}
return fitsIn(dividend, divisor, count + 1, accum + divisor);
}
function divide(dividend: number, divisor: number): number {
let timesFits = fitsIn(Math.abs(dividend), Math.abs(divisor), 0, 0);
return isPositive(dividend) === isPositive(divisor) ? timesFits : -timesFits;
}
console.log(divide(10, 3));
// 3
console.log(divide(-2147483648, -1));
// RangeError: Maximum call stack size exceeded
console.log(divide(10000, 1));
// RangeError: Maximum call stack size exceeded
I tried running this code with TypeScript 4.6.2 in Strict Mode and it caused the stack to overflow. The recursive call is at the end of the function and the accumulation is done inside the recursive function calls. Shouldn't this code be optimized for tail recursion? What should be changed to get tail recursion optimization to occur?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
Typescript不会优化尾部递归功能。请参阅 Microsoft/typeScript#32743 提供权威答案。
通常,JavaScript中的术语“尾声呼叫优化”表示将尾部回复功能重写为迭代版本。这与“尾声消除”有所不同,这通常意味着保持递归,但要重写当前的堆栈框架,而不是将新的堆栈框架推向堆栈。如果您关心的只是堆栈的增长,则两者的行为也相似,但是通常以高于尾巴呼叫消除的抽象水平进行尾声优化。
如果您建议使用Typescript empasion 尾部调用优化作为绩效改进,那么这与打字稿的设计目标。一般而言,如果您为目标运行时环境编写一些句法正确的JavaScript,则编译器将发射该代码为IS。打字稿不应该优化您的JavaScript,而只是发出。因此,没有机会自己做到这一点。
另一方面,您可能正在谈论正确的尾巴呼叫消除,如《 Ecmascript 2015》(ES2015/ES6)规范中所介绍的。此功能的目的是使JavaScript运行时引擎可以检测到尾声,并且在这种情况下不会添加到呼叫堆栈中。
此功能从未被广泛采用; 当前只有基于Safari 。
当运行时引擎设计师考虑实施此功能时,他们会遇到可能的性能退化,开发人员混乱等问题。请参阅例如,此V8博客文章。大多数运行时引擎似乎都选择了一些更理想的版本的“等待观察”方法,例如一种语法可以明确选择进行这种消除。唯一的此类语法的明显建议多年来一直“不活跃”。因此,看起来“等待”的“等待”部分可能会永远持续下去。
虽然typeScript可以
无论好坏,看起来自动尾声消除是一个事实上的死亡功能,而打字稿不会为我们恢复它。
TypeScript does not optimize tail-called recursive functions. See microsoft/TypeScript#32743 for an authoritative answer.
Usually the term "tail call optimization" in JavaScript denotes rewriting a tail-recursive function to an iterative version. This differs somewhat from "tail call elimination" which usually means keeping the recursion but rewriting the current stack frame instead of pushing a new one onto the stack. Both behave similarly from the outside if all you care about is stack growth, but tail call optimization generally happens at a level of abstraction higher than tail call elimination.
If you are suggesting that TypeScript implement tail call optimization as a performance improvement, that's not something that fits with TypeScript's design goals. Generally speaking if you write some code which is syntactically correct JavaScript for your target runtime environment, the compiler will emit that code as-is. TypeScript isn't supposed to optimize your JavaScript, just emit it. So there's no chance it would do this by itself.
On the other hand, you might be talking about proper tail call elimination, as introduced in the ECMAScript 2015 (ES2015/ES6) specification. This feature was intended so that JavaScript runtime engines would detect tail calls and not add to the call stack in such cases.
This feature was never widely adopted; currently only Safari-based browsers seem to consistently do this.
When runtime engine designers looked into implementing this, they ran into issues with possible performance degradation, developer confusion, etc. See this V8 blog post for example. Most runtime engines seem to have opted for a "wait-and-see" approach for some more desirable version of this, such as a syntax to explicitly opt in to such elimination. And the only notable proposal for such syntax has been "inactive" for years. So it looks like the "wait" part of "wait-and-see" might last forever.
While it would be possible for TypeScript to downlevel proper tail call elimination into something like tail call optimization, it would likely run into the same issues, and they have declined to do it.
For better or worse, it looks like automatic tail call elimination is a de-facto dead feature, and TypeScript is not going to revive it for us.
this 可能是您需要的东西:
或以经典风格为:
它们是此Java博客。
它只是将一个尾声转换为一段时间循环。
来自此 pure.ts 的代码。
This might be the thing you need:
or this which is in classic style:
They're two type of TS mock by this java blog.
It just trans a tailcall into a while loop.
Codes from this pure.ts.