Generators overview

Оновлено: 11.05.2023

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

Генератори надають простий спосіб реалізації простих ітераторів без накладних витрат або складності реалізації класу, який реалізує інтерфейс Iterator.

Генератор дозволяє писати код, який використовує foreach для ітерації над набором даних без необхідності створювати масив у пам'яті, що може призвести до перевищення ліміту пам'яті або вимагати значної кількості часу для генерації. Замість цього ви можете написати функцію-генератор, яка працює так само, як і звичайна функція, за винятком того, що замість того, щоб повернутися один раз, генератор може видавати результат стільки разів, скільки потрібно, щоб отримати значення для ітерації.

Простим прикладом цього є перевизначення функції 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('Step must be positive');
        }

        for ($i = $start; $i <= $limit; $i += $step) {
            yield $i;
        }
    } else {
        if ($step >= 0) {
            throw new LogicException('Step must be negative');
        }

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

/*
 * Note that both range() and xrange() result in the same
 * output below.
 */

echo 'Single digit odd numbers from range():  ';
foreach (range(1, 9, 2) as $number) {
    echo "$number ";
}
echo "\n";

echo 'Single digit odd numbers from xrange(): ';
foreach (xrange(1, 9, 2) as $number) {
    echo "$number ";
}
?>

При виклику функції-генератора повертається новий об'єкт внутрішнього класу Generator. Цей об'єкт реалізує інтерфейс Iterator так само, як і об'єкт ітератора з прямим доступом, і надає методи, які можна викликати для маніпулювання станом генератора, включаючи надсилання значень до нього та повернення значень з нього.