Почему мой генератор становится пустым после повторения?

У меня есть генератор, возвращаемый мне вызовом функции из библиотеки, которую я использую. Затем я передаю этот генератор функции, которая перебирает его и выполняет кучу логики для каждого из элементов. Затем я хочу обратиться к этому же генератору после вызова этой функции. Однако, похоже, генератор больше не имеет/не генерирует никаких элементов. Код выглядит следующим образом:

let myGenerator = this.generatorFunc();
console.log(Array.from(myGenerator).length); //prints N which is specified elsewhere
this.iterateThroughGenerator(myGenerator);
console.log(Array.from(myGenerator).length); //now prints 0 when I need it to be N still

iterateThroughGenerator(generator) {
    for(let element of generator) {
        // do a bunch of stuff with element
    }
}

person TomLisankie    schedule 28.06.2019    source источник
comment
Какое определение для this.getGeneratorFunc()?   -  person Patrick Roberts    schedule 28.06.2019
comment
@PatrickRoberts вот определение в библиотеке, которую я использую. В основном просто используется для создания набора полигонов. github.com/d3/d3-delaunay/blob/   -  person TomLisankie    schedule 28.06.2019
comment
В этом случае getGeneratorFunc() плохо назван. Эта функция является функцией-генератором. Он возвращает итератор генератора, который можно использовать только один раз.   -  person Patrick Roberts    schedule 28.06.2019
comment
@PatrickRoberts Правильно, определенно неправильное название. Будет обновляться. Можете ли вы объяснить, почему итератор можно использовать только один раз? Конечно, бывают ситуации, когда вы захотите повторно использовать итератор.   -  person TomLisankie    schedule 28.06.2019
comment
Поскольку итератор генератора имеет внутреннее состояние, чтобы отслеживать, с какой точки потока управления его функцией генератора возобновлять работу при каждом вызове next(). Как только его внутреннее состояние достигает конца функции, next() просто возвращает { value: undefined, done: true }, как объясняет Берги в своем ответе.   -  person Patrick Roberts    schedule 28.06.2019
comment
@PatrickRoberts Хорошо, понятно. Но зачем кому-то это? Зачем нужны итераторы, когда можно просто использовать итераторы? Это просто для сохранения памяти, когда вы знаете, что вам нужно будет перебрать коллекцию только один раз?   -  person TomLisankie    schedule 28.06.2019
comment
Назначение итераторов не в том, что они повторяют коллекцию один раз, а в том, что их можно использовать постепенно, потому что они сохраняют состояние. Тот факт, что они повторяют базовую коллекцию только один раз, является просто ограничением сохранения состояния.   -  person Patrick Roberts    schedule 28.06.2019


Ответы (4)


Так работают итераторы. Вызов генератора возвращает итератор, который можно повторить один раз. То же самое и в большинстве других языков.

let generator = function* () {
  for (let i = 0; i < 3; i++) 
    yield i;
};

let iterator = generator();

console.log(Array.from(iterator)); // [1...3]
console.log(Array.from(iterator)); // []

console.log(Array.from(generator())); // [1..3]
console.log(Array.from(generator())); // [1..3]

person junvar    schedule 28.06.2019
comment
Я понимаю. Почему итераторы могут повторяться только один раз? Должны быть обстоятельства, когда вам нужен итератор, который вы сможете использовать несколько раз. Есть ли какое-то свойство для итераторов, которое позволило бы этому произойти? - person TomLisankie; 28.06.2019
comment
Моя догадка для простоты; большинство реализаций итераторов включают методы bool hasNext() и T next(), но не метод void restart(). Если вы еще этого не сделали, в качестве забавной практической задачи попробуйте реализовать итератор. Также см. stackoverflow.com/ вопросы/7689261/ - person junvar; 28.06.2019
comment
@TomLisankie Итератор - это объект с состоянием, вы повторяете его один раз, пока он не закончится, а затем выбрасываете его. Если вы хотите снова повторить свой итерируемый объект (массив, что угодно,...), вы создаете новый итератор. (Конечно, можно было бы написать итератор, который сбрасывается, когда он закончился, но это не так, как они обычно работают и потребляются). - person Bergi; 28.06.2019

Другие уже объяснили, почему это происходит, но я все равно повторю.

Функции-генераторы возвращают объект-генератор, реализующий как итерируемый протокол (Symbol.iterator), используя простую циклическую ссылку на себя, а протокол итератора (a next()), чтобы выполнять итерацию с сохранением состояния потока управления его функцией-генератором.

Чтобы решить вашу проблему, инкапсулируйте вызов функции генератора с объектом, который реализует итерируемый протокол, возвращая отдельные экземпляры вашего объекта генератора, и вы можете использовать его таким же образом:

const iterable = { [Symbol.iterator]: () => this.generatorFunc() };

console.log(Array.from(iterable).length); //prints N which is specified elsewhere
this.iterateThroughGenerator(iterable);
console.log(Array.from(iterable).length); //still prints N

iterateThroughGenerator(iterable) {
    for(let element of iterable) {
        // do a bunch of stuff with element
    }
}
person Patrick Roberts    schedule 28.06.2019

Как только функция генератора завершена, вам нужно вызвать this.getGeneratorFunc(), чтобы снова создать генератор. Кроме того, когда вы делаете Array.from(myGenerator), он также завершает этот генератор, поэтому, когда вы вызываете this.iterateThroughGenerator(myGenerator), ничего не происходит, потому что из генератора больше не возвращаются элементы. Таким образом, вы можете либо сохранить результат генератора в массив и повторно использовать этот массив, либо вызывать this.getGeneratorFunc() три раза каждый раз, когда вы хотите получить из него элементы. В этом конкретном примере я бы сделал

 const generated = Array.from(this.getGeneratorFunc());
 console.log(generated.length);
 this.iteratedItems(generated);
 console.log(generated.length);

Проверьте и этот ответ. Предыдущий ответ
Я также читал это.

person Nicholas Harder    schedule 28.06.2019

Array.from() исчерпывает генератор (повторяя его до конца), и дальнейшая итерация не даст больше элементов. Вызовы next() всегда будут возвращать {value: undefined, done: true}.

Чтобы создать новый генератор, который снова запускается с самого начала, вам нужно снова вызвать generatorFunc().

person Bergi    schedule 28.06.2019