【问题标题】:Javascript ES6: Implementing generator for unfold functionJavascript ES6:为展开函数实现生成器
【发布时间】:2020-08-08 16:16:44
【问题描述】:

我正在尝试重构此代码,该代码定义了一个 unfold 函数并使用它来创建 count 函数,该函数将一个数组填充到 count 为止。我不想调用count(100),而是想将count变成一个生成器,可以通过任意调用next()来使用。

function unfold (fn, state) {
    return fn( 
        (value, nextState) => {
            return [ value, ...unfold (fn, nextState)]
        },
        ()=>[],
        state
    );
}

function count (max) {
    return unfold(
        (next, done, state)=>{
            return state >= max ?
            done() :
            next(state, state +1)
        }, 
        0
    );
}

这里的流程已经有点难以理解,我很难弄清楚 yield 语句的流程应该如何工作。我想产生结果数组,它是 unfold 函数 return [ value, ...unfold (fn, nextState)] 的第 4 行,但我不知道如何将它一直传递给 count 函数。

这是我目前所拥有的,但它只是返回一个带有一个生成器的生成器,然后在几个 next 调用后结束:

function * _unfold (fn, base) {
    yield * fn(
        (value, nextState)=>([ value, ..._unfold (fn, nextState)]),
        base
    )

    return [];
}

function * count (max) {

    yield * _unfold(
        compress,
        0
    );
    return 0;

}

function * compress (next, state) {
    yield next(state, state +1)
    return null;
}

【问题讨论】:

    标签: javascript ecmascript-6 functional-programming generator


    【解决方案1】:

    我想向您展示一个尽可能接近 FP 中的原始展开实现的实现。希望从那里您可以使用命令式生成器来实现它。

    这是unfoldr的第一个版本:

    unfoldr = f => state => {
      const go = ([x, state_]) =>
        state_ === undefined
          ? []
          : arrCons(x) (go(f(state_)));
    //                  ^^^^^^^^^^^^^ strictly evaluated
    
      return go(f(state));
    };
    

    展开是一个本质上是无限的过程,因此你需要惰性来阻止它。更准确地说,您需要一个构建结构的函数,该结构的第二个参数是非严格的。 arrCons 在两个参数中都可以是非严格的,因为它所做的只是将它们存储在类似对的数据类型中。但是,Javascript 是经过严格评估的。

    假设我们有一个函数thunk,它为 Javascript 引入了一个隐式的 thunk,也就是说,一个空函数,你可以像对象上的惰性 getter 一样在不带括号的情况下调用它。它只需要一个普通的空函数并将其转换为隐式函数。这是我们更新的unfoldr:

    unfoldr = f => state => {
      const go = ([x, state_]) =>
        state_ === undefined
          ? []
          : arrCons(x) (thunk(() => go(f(state_))));
    
      return go(f(state));
    };
    

    现在我们模拟非严格评估,递归步骤中的表达式被评估得恰到好处,即简化为 [x, Thunk] 形式

    仅此而已。请注意,我们使用[] 表示基本情况,从而表示展开过程的结束。我们宁愿用标记的联合来编码这种行为,即Option/Maybe 类型。但为了简单起见,我将实现保持原样。

    这是一个通过定义斐波那契数列如何使用unfoldr 的示例:

    const arrCons = head => tail =>
      [head, tail];
    
    const unfoldr = f => state => {
      const go = ([x, state_]) =>
        state_ === undefined
          ? []
          : arrCons(x) (thunk(() => go(f(state_))));
    
      return go(f(state));
    };
    
    const fibs = unfoldr(
      ([x, y]) => [x, [y, x + y]]) ([0, 1]);
    
    const main = fibs[1] [1] [1] [1] [1] [1] [1] [1] [1] [1]; // [55, Thunk]
    
    main[0]; // 55
    

    这里是完整的实现,thunk 返回一个Proxy:

    const thunk = f =>
      new Proxy(f, new ThunkProxy(f));
    
    const THUNK = "scriptum_thunk";
    
    class ThunkProxy {
      constructor(f) {
        this.memo = undefined;
      }
    
      apply(g, that, args) {
        if (this.memo === undefined)
          this.memo = g();
    
        return this.memo(...args);
      }
    
      defineProperty(g, k, descriptor) { debugger;
        if (this.memo === undefined)
          this.memo = g();
    
        Object.defineProperty(this.memo, k, descriptor);
        return true;
      }
    
      get(g, k) {
        if (this.memo === undefined)
          this.memo = g();
    
        if (k === THUNK)
          return true;
    
        else if (k === Symbol.toPrimitive)
          return () => this.memo;
    
        else if (k === "valueOf")
          return () => this.memo;
    
        else return this.memo[k];
      }
    
      has(g, k) {
        if (this.memo === undefined)
          this.memo = g();
    
        return k in this.memo;
      }
    
      set(g, k, v) {
        if (this.memo === undefined)
          this.memo = g();
    
        this.memo[k] = v;
        return true;
      }  
    }
    
    const arrCons = head => tail =>
      [head, tail];
    
    const arrUnfoldr = f => state => {
      const go = ([x, state_]) =>
        state_ === undefined
          ? []
          : arrCons(x) (thunk(() => go(f(state_))));
    
      return go(f(state));
    };
    
    const fibs = arrUnfoldr(
      ([x, y]) => [x, [y, x + y]]) ([0, 1]);
    
    const main = fibs[1] [1] [1] [1] [1] [1] [1] [1] [1] [1]; // [55, Thunk]
    
    console.log(main[0]);

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-08
      • 2014-02-13
      • 2016-10-14
      • 1970-01-01
      • 1970-01-01
      • 2017-11-20
      • 1970-01-01
      • 2020-02-09
      相关资源
      最近更新 更多