Javascript ES6: реализация генератора для функции раскрытия - PullRequest
0 голосов
/ 25 апреля 2020

Я пытаюсь реорганизовать этот код, который определяет функцию unfold и использует ее для создания функции 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
    );
}

Поток здесь уже довольно сложен для понимания, и у меня непросто понять, как должен работать поток отчетов о доходности. Я хочу получить массив результатов, который является строкой 4 функции unfold return [ value, ...unfold (fn, nextState)], но я не уверен, как передать этот результат до функции 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;
}

1 Ответ

1 голос
/ 25 апреля 2020

Я хочу показать вам реализацию, максимально приближенную к исходной реализации развертывания в 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, которая вводит неявный thunk в Javascript, то есть нулевую функцию, которую можно вызывать без скобок, как ленивый метод получения на объекте. Он просто берет нормальную нулевую функцию и преобразует ее в неявную. Вот наш обновленный unfoldr:

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

  return go(f(state));
};

Теперь, когда мы mimi c нестрогая оценка, выражение в рекурсивном шаге вычисляется достаточно просто, то есть приводится к форме [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]);
...