Javascript ES6:实现展开功能的生成器

问题描述 投票:0回答:1

我正在尝试重构此代码,该代码定义了一个unfold函数,并使用它来制作count函数,该函数将一个数组填充为最大数量的数字。我不想把count(100)变成一个生成器,可以通过任意调用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个回答
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一样在没有括号的情况下调用一个空函数。它只需要一个普通的null函数并将其转换为隐式函数即可。这是我们更新的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]);
© www.soinside.com 2019 - 2024. All rights reserved.