简体   繁体   English

表达一个专门用于列表作为命令循环的futumorphism

[英]Express a futumorphism specialized to lists as an imperative loop

I've been trying to translate this recursive Haskell implementation of a futumorphism specialized to List s 我一直在尝试将这种递归的Haskell实现转换为专门用于List的futumorphism

futuL :: (a -> Maybe (b, ([b], Maybe a))) -> a -> [b]
futuL f x = case f x of
  Nothing -> []
  Just (y, (ys, mz)) -> y : (ys ++ fz)
    where fz = case mz of
      Nothing -> []
      Just z -> futuL f z

into an imperative Javascript loop. 进入命令式Javascript循环。 It is remarkably difficult (at least for me): 这非常困难(至少对我而言):

const None = ({type: "Option", tag: "None"});
const Some = x => ({type: "Option", tag: "Some", runOption: x});

const arrFutu = coalg => x => { // futuL f x
  const acc = []; // ~ fz

  while (true) {
    let optX = coalg(x); // f x

    switch (optX.tag) { // case
      case "None": return acc; // Nothing -> []

      case "Some": {
        let [y, [ys, optX_]] = optX.runOption; // Just (y, (ys, mz))

        switch(optX_.tag) {
          case "None": {
            arrPushFlat(acc) ((ys.unshift(y), ys)); // y : (ys ++ [])
            return acc;
          }

          case "Some": { // y : (ys ++ futuL f z)
            arrPushFlat(acc) ((ys.unshift(y), ys)); 
            x = optX_.runOption;
            break;
          }

          default: throw new UnionError("invalid tag");
        }

        break;
      }

      default: throw new UnionError("invalid tag");
    }
  }
};

I have a difficult time to parse the Haskell code mentally, especially the where part containing the recursive call. 我很难在心理上解析Haskell代码,尤其是包含递归调用的where部分。 I guess this call isn't in tail position, therefore I need an explicit stack ( acc ) for my JS loop. 我猜这个调用不在尾部位置,因此我需要为我的JS循环显式堆栈( acc )。

Is my translation correct? 我的翻译是否正确?

Because Haskell is lazy, one can start consuming the beginning of the list returned by the "futu" before the rest has been calculated. 因为Haskell是懒惰的,所以可以在计算其余部分之前开始使用“futu”返回的列表的开头。 In Javascript terms, this is best modeled with generators . 在Javascript术语中,这最好用生成器建模。

For example (for simplicity, I used null values to represent the None s): 例如(为简单起见,我使用null值来表示None ):

const arrFutu = coalg => function*(seed) {
    while (true) {
        const next = coalg(seed);
        if (next) {
            // Maybe (b, ([b], Maybe a)), using null for None & flattening nested tuple   
            const [item, items, nextseed] = next; 
            yield item;
            yield* items;
            // Maybe a, using null for None 
            if (nextseed) { 
                seed = nextseed;
                continue; // Continue iterating if the seed isn't null.
            }
        } 
        return;
    }
}

With an example coalgebra like: 以示例代数为例:

const coalg1 = seed => {
    if (seed < 5) {
        return ['a', ['a','a'], seed + 1];
    } else if (seed < 10) {
        return ['b', ['b','b'], seed + 1];
    } else if (seed == 10) {
        return ['c', ['c'], null];
    }
    return null;
}

for (const x of arrFutu(coalg1)(0)) {
    console.log(x);
}

for (const x of arrFutu(coalg1)(20)) {
    console.log(x);
}

It would be nice to make the futu recursive instead of iterative, but it seems Javascript tail call optimization doesn't work with generators . 使futu递归而不是迭代会很好,但似乎Javascript尾调用优化不适用于生成器


By the way, even if the Haskell code isn't tail recursive, the recursion is "guarded" : the recursive call happens within one or more data constructors (here, list constructors) and the call can be delayed until the constructors have been "peeled away" by the client when he is consuming the returned list. 顺便说一句,即使Haskell代码不是尾递归,递归也是“保护的” :递归调用发生在一个或多个数据构造函数(这里是列表构造函数)中,并且调用可以延迟直到构造函数已经“客户在消费退回的清单时“剥去了”。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM