Введение

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

Применение

Генераторы обычно вызываются внутри циклов. При первом достижении вызова генератора в цикле создается объект итератора, который инкапсулирует состояние процедуры генератора в начале, с аргументами, привязанными к соответствующим параметрам. Затем тело генератора выполняется в контексте этого итератора до тех пор, пока не встретится специальная операция `yield`; в этот момент значение, предоставленное с операцией `yield`, используется как значение выражения вызова. При следующем достижении того же вызова генератора в последующей итерации выполнение тела генератора возобновляется после операции `yield`, пока не встретится очередная операция `yield`. Помимо операции `yield`, выполнение тела генератора также может быть завершено операцией `finish`, при которой завершается ближайший цикл, охватывающий вызов генератора. В более сложных ситуациях генератор может быть использован вручную вне цикла для создания итератора, который затем можно использовать различными способами. Поскольку генераторы вычисляют свои значения только по требованию, они полезны для представления потоков данных, таких как последовательности, которые было бы дорого или невозможно вычислить сразу. К ним относятся, например, бесконечные последовательности и потоки данных в реальном времени. Когда требуется немедленное вычисление (преимущественно, когда последовательность конечна, поскольку в противном случае вычисление никогда не завершится), можно либо преобразовать последовательность в список, либо использовать конструкцию, которая создает список вместо генератора. Например, в Python генератор `g` можно преобразовать в список `l` с помощью `l = list(g)`, а в F# выражение последовательности `seq { }` вычисляется лениво (как генератор или последовательность), но `[ ]` вычисляется немедленно (как список). При наличии генераторов, конструкции циклов языка, такие как `for` и `while`, можно свести к единой конструкции `loop ... end loop`; все обычные конструкции циклов можно затем удобно смоделировать, используя подходящие генераторы. Например, цикл по диапазону, такой как `for x = 1 to 10`, можно реализовать как итерацию через генератор, как в Python: `for x in range(1, 10)`. Кроме того, оператор `break` можно реализовать как отправку сигнала `finish` генератору, а затем использование оператора `continue` в цикле.

Хронология

Генераторы впервые появились в CLU (1975), были важной особенностью языка манипулирования строками Icon (1977) и теперь доступны в Python (2001), C#, Ruby, PHP, ECMAScript (начиная с ES6/ES2015) и других языках. В CLU и C# генераторы называются итераторами, а в Ruby – перечислителями.

Липс

Окончательный стандарт Common Lisp не предусматривает генераторы нативно, однако существует множество библиотечных реализаций, таких как SERIES, описанная в CLtL2, или pygen.

С

В C нет генераторных функций как встроенной языковой конструкции, но поскольку они являются подмножеством корутин, их легко реализовать, используя любую библиотеку, поддерживающую корутины с сохранением стека, например, libdill. На платформах POSIX, если стоимость переключения контекста на каждой итерации несущественна или требуется полный параллелизм, а не просто конкурентность, можно реализовать очень простую библиотеку генераторных функций с помощью pthreads и каналов.

Python (англ.)

Генераторы были добавлены в Python в версии 2.2 в 2001 году.