Подтвердить что ты не робот

Как клонировать генератор ES6?

Я пытаюсь создать монаду List в ES6 с помощью генераторов. Чтобы заставить его работать, мне нужно создать копию итератора, который уже потреблял несколько состояний. Как клонировать итератор в ES6?

function* test() {
    yield 1;
    yield 2;
    yield 3;
}

var x = test();
console.log(x.next().value); // 1
var y = clone(x);
console.log(x.next().value); // 2
console.log(y.next().value); // 2 (sic)

Я пробовал clone и cloneDeep из lodash, но они бесполезны. Итераторы, возвращаемые таким образом, являются родными функциями и сохраняют свое внутреннее состояние, поэтому кажется, что нет никакого способа сделать это с помощью собственного кода JS.

4b9b3361

Ответ 1

Итераторы [...] сохраняют свое внутреннее состояние, поэтому, кажется, нет способа

Да, и это по уважительной причине. Вы не можете клонировать состояние, иначе вы могли бы слишком сильно повлиять на генератор.

Возможно, однако, возможно создать второй итератор, который работает рядом с первым, путем запоминания его последовательности и последующего его последующего повторения. Однако должен быть только один итератор, который действительно управляет генератором - в противном случае, какой из ваших клонов будет разрешен для отправки аргументов next()?

Ответ 2

Я написал библиотеку do-notation для JavaScript, burrido. Чтобы обойти проблему с изменяемым генератором, я сделал immutagen, который эмулирует неизменный генератор, сохраняя историю входных значений и воспроизводя их для клонирования генератор в любом конкретном состоянии.

Ответ 3

Вы не можете клонировать генератор - это просто функция без состояния. Что может иметь состояние и, следовательно, что можно клонировать, это iterator, вызванный вызовом функции-генератора.

Этот подход кэширует промежуточные результаты, так что клонированные итераторы могут обращаться к ним в случае необходимости, пока они не "догонят". Он возвращает объект, который является итератором и итерируемым, поэтому вы можете либо называть next на нем, либо for...of над ним. Любой итератор может быть передан, поэтому теоретически можно клонировать итераторы по массиву, передавая в array.values(). Какой бы клон не вызывал next сначала в данной точке итерации, аргумент передавался next, если таковой был, отражен в значении yield в базовом генераторе.

function clonableIterator(it) {
  var vals = [];

  return function make(n) {
    return {
      next(arg) {
        const len = vals.length;
        if (n >= len) vals[len] = it.next(arg);
        return vals[n++];
      },
      clone()   { return make(n); },
      throw(e)  { if (it.throw) it.throw(e); },
      return(v) { if (it.return) it.return(v); },
      [Symbol.iterator]() { return this; }
    };
  }(0);
}

function *gen() {
  yield 1;
  yield 2;
  yield 3;
}

var it = clonableIterator(gen());

console.log(it.next());
var clone = it.clone();
console.log(clone.next());
console.log(it.next());