Генераторы и оператор yield
Генератор
предоставляет функцию, которая генерирует набор значений.
Для возвращения значения из функции применяется оператор 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;
}
При переборе в цикле мы фактически перебираем результат функции как обычный массив, каждый элемент которого имеет числовой индекс, начиная с нуля:
<?
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
}
Дело в том, что при работе с массивом весь массив загружается в память. При небольших объемах проблема может быть игнорироваться. Но чем больше размер массива, соответственно тем больше издержки и потери в производительности. Именно эту проблему и призваны решить генераторы, которые извлекают только одно значение одномоментно при обращении к функции, экономя тем самым вычислительные ресурсы.