Как клонировать генератор 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.
Ответы
Ответ 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());