Javascript fix可以是尾部递归的,因此可以表示为一个简单的循环吗?

Javascript fix可以是尾部递归的,因此可以表示为一个简单的循环吗?,javascript,recursion,functional-programming,fixpoint-combinators,Javascript,Recursion,Functional Programming,Fixpoint Combinators,我不知道如何将fix表示为尾部递归算法。或者它已经是尾部递归的了?我可能想得太多了 const fix=f=>x=>f(fix(f))(x);//由于急于评估而导致的冗余eta抽象 const sum=fix(go=>acc=>([x,…xs])=> x==未定义 ?acc :go(acc+x)(xs))(0); 常量main=总和([1,2,3,4,5]); console.log(主);//15 我不知道如何将fix表示为尾部递归算法 首先,将fix的定义转换为 //修复::((a->b)

我不知道如何将
fix
表示为尾部递归算法。或者它已经是尾部递归的了?我可能想得太多了

const fix=f=>x=>f(fix(f))(x);//由于急于评估而导致的冗余eta抽象
const sum=fix(go=>acc=>([x,…xs])=>
x==未定义
?acc
:go(acc+x)(xs))(0);
常量main=总和([1,2,3,4,5]);
console.log(主);//15
我不知道如何将
fix
表示为尾部递归算法

首先,将
fix
的定义转换为

//修复::((a->b)->a->b)->a->b
常数fix=f=>x=>{
常数g=固定值(f);
常数h=f(g);
常数y=h(x);
返回y;
};
接下来,将生成的程序转换为

//输入contr a=(a->r)->r
//修正::((a->contrb)->contr(a->contrb))->contr(a->contrb)
常数fix=f=>k=>k(x=>k=>
固定值(f)(g=>
f(g)(h=>
h(x)(y=>
k(y)("));;
现在,
fix
是尾部递归的。然而,这可能不是你想要的

尾部递归算法可以很容易地转换为堆栈安全循环。这才是真正的目标

如果您想要的只是堆栈安全,那么可以使用monad

//弹跳::(a->蹦床b)->a->蹦床b
常量Bounce=func=>(…args)=>({Bounce:true,func,args});
//返回::a->蹦床a
const Return=value=>({bounce:false,value});
//蹦床:蹦床a->a
康斯特蹦床=结果=>{
而(result.bounce)result=result.func(…result.args);
返回result.value;
};
//固定::((a->蹦床b)->a->蹦床b)->a->蹦床b
常数fix=f=>Bounce(x=>f(fix(f))(x));
//id::a->a
常数id=x=>x;
//可达码
console.log(“开始”);//在浏览器中打开控制台
//无限循环
蹦床(固定(id)(0));
//不可达代码

控制台日志(“结束”)我将在这里留下这个链接,以防它对您的目标有任何影响@MichaelBianconi不管JS环境是否支持TCO-任何尾部递归调用都可以通过蹦床或手动方式轻松转换为循环。我所能说的是,这目前不是尾部递归,因为最后一次执行是
f
而不是
fix
。我不知道是否有可能使它成为尾部递归的,但是我很想知道。我知道了
fix
是一种功能化蹦床。唯一的区别是,
fix
支持真正的匿名递归,而命令式蹦床只支持作为循环实现的伪递归。或者它已经是尾部递归了?不,不是尾部递归。一个简单的反例是
fix(id)(0)
。如果
fix
是尾部递归的,那么它将进入无限循环。相反,它会导致堆栈溢出。cps转换过程中的中间步骤(a-范式)非常有用。为什么我们需要单子递归来修复
fix
循环
/
重复
/
返回
模式是否足够?当然可以。您可以将
fix
定义为
f=>loop(f(Recur))
。但是,这并不能给您带来太多好处,因为您可以直接使用
循环
/
重现
/
返回
模式。根本不需要
fix
。公平地说,您也不需要
fix
版本的
Trampoline。你可以直接使用蹦床。但是,它比
修复
循环
版本要简单得多。此外,它更接近您对
fix
的原始定义。我刚刚将
Bounce
应用于内部lambda抽象。
fix的
loop
版本与您最初对
fix
的定义完全不同。