Генераторы и итераторы
Часть раздела Объектно-ориентированное программирование путешествия по 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Ключевым преимуществом является эффективность использования памяти. Вместо создания целого массива в памяти, генераторы создают значения по запросу. Это критически важно при работе с большими наборами данных — генератор, обрабатывающий миллион записей, потребляет столько же памяти, сколько и генератор, обрабатывающий десять.
Генераторы также могут возвращать пары ключ-значение:
<?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. Однако в большинстве случаев генераторы предлагают более чистое решение с меньшим количеством шаблонного кода. Они особенно полезны в ООП, когда классу необходимо предоставить коллекцию элементов без загрузки всего объема данных в память сразу.
Задание
ЛегкоДавайте создадим систему инвентаризации товаров, которая использует генераторы для эффективного перебора больших коллекций без одновременной загрузки всего объема данных в память. Вы создадите класс, который хранит товары и предоставляет методы-генераторы для итерации по ним различными способами.
Вы организуете свой код в трех файлах:
Product.php— Создайте классProduct, который представляет собой товар в инвентаре. Используйте продвижение свойств в конструкторе (constructor promotion), чтобы определить публичное свойство$sku(string), публичное$name(string) и публичное$price(float).Inventory.php— Создайте классInventory, который управляет коллекцией товаров и предоставляет методы-генераторы для итерации по ним. Подключите файл Product. Класс должен:- Иметь приватное свойство-массив для хранения товаров
- Иметь метод
addProduct(Product $product), который добавляет товар в коллекцию - Иметь метод-генератор
allProducts(): Generator, который возвращает (yield) каждый товар по одному за раз - Иметь метод-генератор
expensiveProducts(float $minPrice): Generator, который возвращает только те товары, цена которых больше или равна заданному минимуму, возвращая пары ключ-значение, где ключом является SKU, а значением — объект товара
main.php— Подключите файл Inventory. Вы получите один входной параметр: порог минимальной цены.Создайте экземпляр
Inventoryи добавьте эти три товара:- SKU:
"A001", Name:"Laptop", Price:999.99 - SKU:
"A002", Name:"Mouse", Price:29.99 - SKU:
"A003", Name:"Monitor", Price:349.99
Сначала выполните итерацию через
allProducts()и выведите название каждого товара на отдельной строке.Затем выведите пустую строку, после чего выполните итерацию через
expensiveProducts(), используя пороговое значение из входных данных. Для каждого дорогого товара выведите"[sku]: [name] - $[price]"на отдельной строке. Отформатируйте цену ровно с 2 знаками после запятой.- SKU:
Эта задача демонстрирует, как генераторы позволяют выполнять ленивую итерацию по коллекциям — каждый товар возвращается только по запросу, что делает этот подход эффективным с точки зрения использования памяти для больших инвентарных списков. Возврат пар ключ-значение в 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 знаками после запятой
?>В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово $thisМетодыСвойстваКонструктор (__construct)Деструктор (__destruct)Итоги — Простой калькулятор4Наследование
Основы наследованияКлючевое слово parent::Переопределение методовКлючевое слово finalАбстрактные классыИтоги — Иерархия сотрудников7Инкапсуляция
Public, Protected, PrivateПодробный разбор модификаторов доступаГеттеры и сеттерыСокрытие данныхConstructor Promotion (8.0)Итоги — Система учета студентов10Продвинутые концепции ООП
Композиция против наследованияВнедрение зависимостейАнонимные классыПеречисления (PHP 8.1)Файберы (PHP 8.1)Глубокое погружение в клонирование объектовГенераторы и итераторы13Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий2Пространства имен и автозагрузка
Введение в пространства именКлючевое слово useСтандарт автозагрузки PSR-4Автозагрузчик ComposerИтоги: структурированный проект5Интерфейсы и контракты
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовИнтерфейс против абстрактного классаType Hinting с интерфейсамиИтоги — Калькулятор фигур8Магические методы
Введение в магические методы__toString и __debugInfo__get, __set, __isset, __unset__call и __callStatic__clone и клонирование объектов__serialize и __unserializeИтоги — Кастомная коллекция11Система типов и обработка ошибок
Объявление типовNullable-типыТипы объединения и пересеченияКлассы исключенийИерархия пользовательских исключенийTry, Catch, FinallyИтоги — Валидатор форм14Проект: Управление библиотекой
Обзор проектаКлассы Book и User3Свойства классов
Свойства экземпляра и статические свойстваКонстанты в классахСтатические методы и свойстваПриватные и защищенные свойстваСвойства Readonly (PHP 8.1)Итоги — Менеджер банковского счета6Полиморфизм
Снова о переопределении методовПолиморфизм через интерфейсыType Hinting и Union TypesПозднее статическое связываниеИтоги — Payment Processor9Трейты
Введение в трейтыИспользование нескольких трейтовРазрешение конфликтов трейтовАбстрактные методы в трейтахТрейты и наследование12Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy