Получи случайную криптовалюту за регистрацию!

Что такое генераторы в программировании? Они лениво раз за ра | Goal Gesture программирование, IT

Что такое генераторы в программировании?

Они лениво раз за разом вычисляют новые значения, но не помнят, что было до этого.

В программировании есть инструмент, который позволяет экономить память и при этом обрабатывать огромные массивы данных. Это генераторы. Мы рассмотрим работу генераторов на примере языка Python, но они есть и в других языках.

Классический подход к обработке — итераторы
Допустим, мы хотим вывести числа от 1 до 10 и для этого пишем такой код:

for i in range(1,10):
print(i)

Это один из вариантов реализации цикла. Что делает компьютер, когда обрабатывает такое:

Создаст в памяти область для хранения данных.
Заполнит её числами от 1 до 10.
На каждом шаге цикла компьютер возьмёт новые данные из этой области и выведет их на экран.
При этом компьютер точно знает, какое значение у переменной i было на предыдущем шаге и будет на следующем, потому что все они хранятся в памяти.

Но что, если нам понадобится несколько переменных с диапазоном значений? Например, так:

a = range(1,100)
b = range(1000,2000)
for i in a:
print(a[i-1] + b[i])

Когда мы запустим этот код, то увидим, что компьютер выделил большой кусок памяти для обеих переменных и что можно обратиться к отдельным ячейкам в таких диапазонах. Это удобно, когда нужно постоянно держать под рукой какие-то данные. Но если переменные со счётчиками не понадобятся, то память будет простаивать зря.

Итератор в данном случае — это цикл, который обращается к диапазону значений и берёт по очереди оттуда данные. При этом все данные уже есть в памяти.

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

Генераторы — вычисление данных «на лету»

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

Цикл выполняется нужное количество раз.
На каждом шаге цикла генератор получает какое-то значение, отдаёт его в нужное место и забывает всё напрочь.
Генератор не помнит значение, которое он отдавал до этого, и не знает, что он будет отдавать на следующем шаге. Всё, что у него есть, — данные, которые нужно обработать на текущем шаге.
Память под работу генератора выделяется, только когда он генерирует новые данные. Пока генератор стоит или не выдаёт данные — память не выделяется.
Чаще всего генераторы используют как функции. Каждый раз, когда обращаются к такой функции-генератору, она делает так:

Берёт новую порцию данных из указанного ей источника.
Обрабатывает данные.
Возвращает результат.
Забывает про всё до следующего вызова.
Обычно функции возвращают результат своей работы с помощью команды return (), а для генераторов есть специальная команда — yield ().

Yield () работает так же, как и return (), только функция на ней не заканчивается, а ставится на паузу. При следующем вызове генератор возьмёт новую порцию данных, и единственное, что он помнит, — на каком месте он остановился в прошлый раз. Всё остальное генератор каждый раз считает заново.