mail to Michael Grinfeld

Функциональное программирование с PHP-генераторами



Генераторы классные. Они облегчают написание итераторов, определяя функции вместо создания целых классов, реализующих Iterator. Также генераторы помогают создавать ленивые списки (lazy list) и бесконечные потоки. Главное отличие функции-генератора от обычной функции в том, что обычная может возвращать только один раз (после этого её исполнение прекращается), а функция-генератор в ходе исполнения способна выдавать несколько значений. При этом в перерывах между возвратами исполнение генератора ставится на паузу до следующего запуска. Поэтому генераторы могут использоваться для создания списков с лениво генерируемыми значениями, то есть каждый элемент в списке вычисляется только в момент востребованности. Изучите в курсе PHP для начинающих онлайн и бесплатно.

Яркий пример разницы между ранним и ленивым генерированием — функция range, которая берёт параметры start и end, а затем возвращает последовательность целочисленных значений, которая начинается со start и заканчивается за один элемент до end. В случае с обычной функцией вам придётся создать новый список, добавить в него все элементы, а затем уже вернуть список. При таком подходе range потребляет объём памяти, пропорциональный размеру диапазона. И в зависимости от вашей среды range(1, 10000000) может прибрать себе всю память, выделенную PHP-процессом. И происходит это из-за раннего создания всего списка элементов, ещё до возврата вызывающему.


С помощью функции-генератора можно создать ленивую range, использующую постоянный объём памяти. Это достигается с помощью while-цикла, который передаёт значение начального параметра и затем инкрементирует значение начального аргумента. Когда цикл заканчивается, функция доходит до конца его тела и возвращает значение, тем самым завершая работу генератора. То есть функции-генератору достаточно лишь отслеживать, какое значение последовательности нужно возвращать, а не хранить все значения в памяти. Если продолжить пример, то можно создать функцию-генератор бесконечного диапазона, которая берёт лишь начальный аргумент. В этом случае у нас будет while-цикл с предикатом, всегда равным true, так что цикл никогда не заканчивается. Это позволяет вызывающей функции самой решать, сколько значений считывать из генератора. И если бесконечный генератор будет вызван 100 раз, то он и сгенерирует только 100 значений. Если его больше не вызовут до завершения вызывающей функции, генератор встанет на паузу, и в конце концов сборщик мусора его вычистит. Иными словами, если вы будете с помощью foreach итерировать генератор, создающий бесконечный поток значений, то генератор станет итерироваться бесконечно. В документации PHP неплохо описана работа генераторов и пример с функцией range.











































1b9bfe604bdf79baa0a78141e12d2d38