Знакомство с генераторами

(PHP 5 >= 5.5.0, PHP 7, PHP 8)

Генераторы — легкий способ реализации простых итераторов без дополнительных ресурсов или сложностей, которые связаны с написанием класса, который реализует интерфейс Iterator.

Генератор поддерживает удобную передачу данных в циклы foreach без предварительной загрузки массива в память, что иногда вызывает превышение программой предела памяти или значительно увеличивает время обработки, которое уходит на генерацию результата. Вместо этого пишут функцию-генератор, которая аналогична стандартной функции, за исключением того, что вместо возврата единого значения с результатом работы цикла, генератор через ключевое слово yield умеет выдавать результат столько раз, сколько значений требуется перебрать. Как и итераторы, генераторы не поддерживают произвольный доступ к элементам массива.

Простой пример этого — переопределение функции range() как генератора. Стандартная функция range() генерирует массив, который состоит из значений, и возвращает его, что приводит к генерации больших массивов: например, вызов range(0, 1000000) займёт более 100 МБ оперативной памяти.

В качестве альтернативы можно создать генератор xrange(), который использует память только чтобы создать объект Iterator и сохранить текущее состояние, что потребует не больше 1 килобайта памяти.

Пример #1 Реализация функции range() как генератора

<?php

function xrange($start, $limit, $step = 1)
{
if (
$start <= $limit) {
if (
$step <= 0) {
throw new
LogicException('Шаг должен быть положительным');
}

for (
$i = $start; $i <= $limit; $i += $step) {
yield
$i;
}
} else {
if (
$step >= 0) {
throw new
LogicException('Шаг должен быть отрицательным');
}

for (
$i = $start; $i >= $limit; $i += $step) {
yield
$i;
}
}
}

/* Обратите внимание, что и range() и xrange() дадут один и тот же вывод */

echo 'Нечётные однозначные числа с помощью range(): ';
foreach (
range(1, 9, 2) as $number) {
echo
"$number ";
}
echo
"\n";

echo
'Нечётные однозначные числа через функцию xrange(): ';
foreach (
xrange(1, 9, 2) as $number) {
echo
"$number ";
}

?>

Результат выполнения приведённого примера:

Нечётные однозначные числа через функцию range():  1 3 5 7 9
Нечётные однозначные числа через функцию xrange(): 1 3 5 7 9

Объект Generator

Когда функция-генератор вызывается, она возвращает объект встроенного класса Generator. Этот объект реализует интерфейс Iterator, станет однонаправленным объектом итератора и предоставит методы, с помощью которых можно управлять его состоянием, включая передачу в него и возвращения из него значений.