Генератор предоставляет функцию, которая генерирует набор значений.
Для возвращения значения из функции применяется оператор yield. Но в отличие от return оператор yield сохраняет состояние функции, позволяя ей продолжать работу с того места, когда остановилось ее выполнение.
Например, определим простейшую функцию генератора:
function generator(){ yield 21; }
Здесь функция генератора фактически возвращает только одно число 21. Тем не менее мы можем перебирать результат функции генератора в цикле как стандартный массив:
foreach(generator() as $number) { echo $number; // 21 }
Подобным образом генератор может возвращать и большее количество значений:
function generateNumbers() { for ($i = 0; $i <= 5; $i++) { yield $i; } } foreach(generateNumbers() as $number) { echo $number; // 012345 }
В данном случае функция генератора generateNumbers()
с помощью цикла генерирует значения от 0 до 5 включительно. Это все равно, если бы написали:
function generateNumbers() { yield 0; yield 1; yield 2; yield 3; yield 4; yield 5; }
При переборе в цикле мы фактически перебираем результат функции как обычный массив, каждый элемент которого имеет числовой индекс, начиная с нуля:
<?php function generateNumbers() { for ($i = 10; $i <= 15; $i++) { yield $i; } } foreach(generateNumbers() as $index => $number) { echo "$index - $number<br/>"; // 012345 } ?>
Результат функции:
0 - 10 1 - 11 2 - 12 3 - 13 4 - 14 5 - 15
С помощью оператора from можно определять массив - источник данных для генератора:
function generateNumbers() { yield 1; yield from [2, 3, 4]; yield 5; } foreach(generateNumbers() as $number) { echo $number; // 12345 }
В данном случае функция generateNumbers()
для генерации часть данных берет из массива [2, 3, 4]
с помощью выражения
yield from [2, 3, 4]
.
Функция генератора, как и любая функция может принимать параметры, что позволяет настраивать поведение генератора:
function generateNumbers($start, $end) { for($i = $start; $i < $end; $i++){ yield $i; } } foreach(generateNumbers(4, 9) as $number) { echo $number; // 45678 }
Но естественно может возникнуть вопрос: а зачем нужны генераторы? Разве мы не можем с тем же успехом перебират обычный массив? Например:
$numbers = [1, 2, 3, 4, 5]; foreach($numbers as $number) { echo $number; // 12345 }
Дело в том, что при работе с массивом весь массив загружается в память. При небольших объемах проблема может быть игнорироваться. Но чем больше размер массива, соответственно тем больше издержки и потери в производительности. Именно эту проблему и призваны решить генераторы, которые извлекают только одно значение одномоментно при обращении к функции, экономя тем самым вычислительные ресурсы.