Menu
Coddy logo textTech

Генераторы и итераторы

Часть раздела Объектно-ориентированное программирование путешествия по PHP на Coddy. Урок 62 из 91.

Генераторы предоставляют простой способ создавать итераторы без реализации полного интерфейса Iterator. Функция-генератор использует yield вместо return, чтобы выдавать значения по одному, приостанавливая выполнение между каждым значением.

Вот базовый генератор, который создаёт последовательность чисел:

<?php
function countTo(int $max): Generator {
    for ($i = 1; $i <= $max; $i++) {
        yield $i;
    }
}

foreach (countTo(3) as $number) {
    echo "$number\n";
}

Вывод:

1
2
3

Ключевое преимущество — эффективность использования памяти. Вместо создания всего массива в памяти генераторы создают значения по требованию. Это особенно важно при работе с большими наборами данных: генератор, обрабатывающий миллион записей, использует столько же памяти, сколько генератор, обрабатывающий десять записей.

Generator также могут возвращать пары ключ-значение:

<?php
function userGenerator(): Generator {
    yield 'admin' => 'Alice';
    yield 'editor' => 'Bob';
}

foreach (userGenerator() as $role => $name) {
    echo "$role: $name\n";
}

Вывод:

admin: Alice
editor: Bob

Чтобы сделать собственные классы итерируемыми с помощью foreach, реализуйте интерфейс Iterator. Однако в большинстве случаев генераторы предлагают более элегантное решение с меньшим количеством шаблонного кода. Они особенно полезны в OOP, когда классу нужно предоставить коллекцию элементов, не загружая всё в память одновременно.

challenge icon

Задание

Легко

Давайте создадим систему инвентаризации товаров, которая использует генераторы для эффективной итерации по большим коллекциям без одновременной загрузки всего в память. Вы создадите class, который хранит товары и предоставляет методы-generator для итерации по ним различными способами.

Вы организуете код в трех файлах:

  • Product.php: Создайте class Product, представляющий товар в инвентаре. Используйте продвижение свойств конструктора, чтобы Define публичное $sku (string), публичное $name (string) и публичное $price (float).
  • Inventory.php: Создайте class Inventory, который управляет collection товаров и предоставляет методы-generator для итерации по ним. Подключите файл Product. class должен:
    • Иметь private свойство типа array для хранения товаров
    • Иметь method addProduct(Product $product), который добавляет товар в collection
    • Иметь метод-generator allProducts(): Generator, который выдает каждый товар по одному
    • Иметь метод-generator expensiveProducts(float $minPrice): Generator, который выдает only товары с ценой, большей или равной указанному минимуму, выдавая пары key-value, где key — SKU, а value — товар
  • main.php: Подключите файл Inventory. Вы получите один input: минимальный порог цены.

    Create Inventory и Add эти три товара:

    • SKU: "A001", Name: "Laptop", Price: 999.99
    • SKU: "A002", Name: "Mouse", Price: 29.99
    • SKU: "A003", Name: "Monitor", Price: 349.99

    Сначала Iterate по allProducts() и выведите Name каждого товара в отдельной строке.

    Затем выведите пустую строку, после чего Iterate по expensiveProducts(), используя input-порог. Для каждого дорогого товара выведите "[sku]: [name] - $[price]" в отдельной строке. Format цену с exactly 2 знаками после десятичной точки.

Это задание демонстрирует, как генераторы позволяют лениво итерироваться по коллекциям: каждый товар выдается только при запросе, благодаря чему такой подход эффективно использует память при работе с большими inventory. Выдача пар key-value в expensiveProducts() показывает, как генераторы могут предоставлять структурированные данные для итерации.

Попробуйте сами

<?php

require_once 'Inventory.php';

// Прочитать ввод
$minPrice = floatval(trim(fgets(STDIN)));

// TODO: Создать экземпляр Inventory

// TODO: Добавить три продукта:
// SKU: "A001", Name: "Laptop", Price: 999.99
// SKU: "A002", Name: "Mouse", Price: 29.99
// SKU: "A003", Name: "Monitor", Price: 349.99

// TODO: Пройтись по allProducts() и вывести имя каждого продукта

// TODO: Вывести пустую строку

// TODO: Пройтись по expensiveProducts() используя $minPrice
// Для каждого дорогого продукта вывести "[sku]: [name] - $[price]"
// Форматировать цену ровно с 2 знаками после запятой

?>
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор PHP