Javascript 如何以堆栈安全的方式映射树?

Javascript 如何以堆栈安全的方式映射树?,javascript,recursion,functional-programming,iterator,tail-recursion,Javascript,Recursion,Functional Programming,Iterator,Tail Recursion,递归映射树的方法有多种: constreducetree=(f,node)=>{ 常数go=([x,xs])=>f(x,xs.map(go)); 返回go(节点); }; 常量映射树=(f,节点)=> reduceTree((x,xs)=>Node_f(x,xs),Node); const Node_u=(x,xs)=>([x,xs]); 常量节点=(x,…xs)=>([x,xs]); 常数树=节点(1, 节点(2, 节点(3), 节点(4, 节点(5)), 节点(6), 节点(7, 节点(8

递归映射树的方法有多种:

constreducetree=(f,node)=>{
常数go=([x,xs])=>f(x,xs.map(go));
返回go(节点);
};
常量映射树=(f,节点)=>
reduceTree((x,xs)=>Node_f(x,xs),Node);
const Node_u=(x,xs)=>([x,xs]);
常量节点=(x,…xs)=>([x,xs]);
常数树=节点(1,
节点(2,
节点(3),
节点(4,
节点(5)),
节点(6),
节点(7,
节点(8),
节点(9),
节点(10),
节点(11));
console.log(

映射树(x=>2*x,tree))以格式良好的相互递归对开始-

// map :: (a -> b, Tree a) -> Tree b
const map = (f, [ v, children ]) =>
  Node (f (v), ...mapAll (f, children))

// mapAll :: (a -> b, [ Tree a ]) -> [ Tree b ]
const mapAll = (f, nodes = []) =>
  nodes .map (n => map (f, n))
我们首先删除eager
Array.prototype.map
,它不可能允许尾部形式-

const map = (f, [ v, children ]) =>
  Node (f (v), ...mapAll (f, children))

const mapAll = (f, [ node, ...more ]) =>
  node === undefined
    ? []
    : [ map (f, node), ...mapAll (f, more) ]
接下来添加CPS转换的参数-

const identity = x =>
  x

const map = (f, [ v, children ], k = identity) =>
  mapAll (f, children, newChilds =>        // tail
    k (Node (f (v), ...newChilds)))        // tail

const mapAll = (f, [ node, ...more ], k = identity) =>
  node === undefined
    ? k ([])                               // tail
    : map (f, node, newNode =>             // tail
        mapAll (f, more, moreChilds =>     // tail
          k ([ newNode, ...moreChilds ]))) // tail

在下面自己的浏览器中验证结果

const节点=(x,…xs)=>
[x,xs]
常量标识=x=>
x
常量映射=(f[v,子项],k=标识)=>
mapAll(f,childs,newChilds=>
k(节点(f(v),…newChilds)))
常量mapAll=(f,[node,…more],k=identity)=>
节点===未定义
? k([]))
:map(f,node,newNode=>
mapAll(f,more,moreChilds=>
k([newNode,…moreChilds]))
常数树=
节点
( 1
,节点
( 2
,节点(3)
,节点
( 4
,节点(5)
)
)
,节点(6)
,节点
( 7
,节点(8)
,节点(9)
,节点(10)
,节点(11)
)
)

console.log(map(x=>x*10,tree))
这可能更适合。但是,为什么您会在意它是否会混淆代码?您只需编写一次
reduceTree()
,其复杂性就隐藏在其中。这就是编写高阶函数的要点。使递归函数尾部递归的一种常见方法是通过传递额外的参数来保存累积的结果。我赞同这个问题,但据我所知,仍然只有Safari实现尾部调用优化。无论是循环还是尾部递归,要在没有递归分支的情况下遍历树,您需要一个堆栈。解决方案是。你应该补充一点,CPS实际上并不能保证堆栈安全,相反,你的解决方案会比原来的解决方案有更多的深树问题。@Bergi我想在你的评论到来之前,你已经通过心灵感应进入了我的脑海。我在末尾加了一条注释:你已经更改了原始类型
reduceTree::(a,[b]->b),树a->b
/
mapTree:(a->b),T a->T b
。这有什么特别的原因吗?我想让贴图变成蹦床材质——应该更明确地说明这一点。@NinaScholz是的,CPS太差劲了!但是,这是呈现递归的、依赖堆栈的函数尾部递归的唯一方法。JS应自ES2015起随TCO一起发货。但是我们被这个行业出卖了:D。