如何在没有尾部调用优化的情况下使用函数式编程replacereplacewhile循环?

我在我的JavaScript中尝试更多function的风格; 因此,我已经用循环replace了诸如map和reduce之类的效用函数。 但是,我还没有findwhile循环的function替代,因为tail调用优化通常不适用于JavaScript。 (从我所了解的ES6防止尾部调用溢出堆栈,但不会优化其性能。)

我解释了我在下面试过的,但是TLDR是:如果我没有tail调用优化,while循环实现的function是什么?

我曾经尝试过:

创build一个“while”实用程序function:

function while(func, test, data) { const newData = func(data); if(test(newData)) { return newData; } else { return while(func, test, newData); } } 

由于尾部调用优化不可用,我可以将其重写为:

 function while(func, test, data) { let newData = *copy the data somehow* while(test(newData)) { newData = func(newData); } return newData; } 

然而在这一点上,我觉得我已经使我的代码更复杂/混淆谁使用它,因为我必须拖动一个自定义的实用function。 我看到的唯一的实际优势是它迫使我使循环纯净; 但似乎只是使用一个常规的while循环更直接,并确保我保持一切纯净。

我也试图找出一种方法来创build一个模仿recursion/循环效应的生成器函数,然后使用find或reduce之类的效用函数对其进行迭代。 但是,我还没有想出一个可读的方式来做到这一点。

最后,用实用函数代替循环使得我更加清楚我要完成的任务(比如对每个元素做一件事情,检查每个元素是否通过testing等)。 然而,在我看来,while循环已经expression了我正在尝试完成的事情(例如迭代直到find素数,迭代直到答案被充分优化为止)。

所以毕竟,我的总体问题是:如果我需要一个while循环,我正在编程的function风格,我没有访问尾调用优化,那么什么是最好的策略。

JavaScript中的一个例子

这里是一个使用JavaScript的例子。 目前,大多数浏览器不支持尾部呼叫优化,因此以下片段将失败

 const repeat = n => f => x => n === 0 ? x : repeat (n - 1) (f) (f(x)) console.log(repeat(1e3) (x => x + 1) (0)) // 1000 console.log(repeat(1e5) (x => x + 1) (0)) // Error: Uncaught RangeError: Maximum call stack size exceeded 

序言:我很高兴你到达这个地方的文件。 在阅读naomik伟大的,彻底的和长期的答案后,你的眼睛可能正在燃烧,你的头脑正在融化。 这里很难共存。 无论如何,这是我的两分钱…

一个直截了当的方法与thunk

您可以通过将recursion调用包装到一个thunk中,将任何尾recursion函数转换为堆栈安全版本。 一个thunk只是一个没有参数的函数。

我的方法的目标是重用recursion模式,以便每个熟悉recursion的人都能够快速地识别出堆栈中的伪装:

 const tailrec = f => (...args) => { let g = f(...args); while (g && g.type === tailrec) g = g(); return g; }; const lazy = f => (...args) => { const g = () => f(...args); g.type = tailrec; return g; }; const repeat = n => f => x => { const aux = lazy((n, x) => n === 0 ? x : aux(n - 1, f(x))); return tailrec(aux) (n, x); }; const inc = x => x + 1; console.log( repeat (1e6) (x => x + 1) (0) // 1000000 );