Generadores e iteradores
Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy. Lección 62 de 91.
Los generadores proporcionan una forma sencilla de crear iteradores sin implementar la interfaz completa Iterator. Una función generadora utiliza yield en lugar de return para producir valores uno a uno, pausando la ejecución entre cada valor.
Aquí tienes un generador básico que produce una secuencia de números:
<?php
function countTo(int $max): Generator {
for ($i = 1; $i <= $max; $i++) {
yield $i;
}
}
foreach (countTo(3) as $number) {
echo "$number\n";
}
Salida:
1
2
3La ventaja clave es la eficiencia de memoria. En lugar de construir un array completo en memoria, los generadores producen valores bajo demanda. Esto es crucial al trabajar con conjuntos de datos grandes: un generador que procesa un millón de registros utiliza la misma memoria que uno que procesa diez.
Los generadores también pueden producir pares clave-valor:
<?php
function userGenerator(): Generator {
yield 'admin' => 'Alice';
yield 'editor' => 'Bob';
}
foreach (userGenerator() as $role => $name) {
echo "$role: $name\n";
}
Salida:
admin: Alice
editor: BobPara hacer que tus propias clases sean iterables con foreach, implementa la interfaz Iterator. Sin embargo, en la mayoría de los casos, los generadores ofrecen una solución más limpia con menos código repetitivo. Son especialmente útiles en la POO cuando una clase necesita exponer una colección de elementos sin cargarlo todo en la memoria de una sola vez.
Desafío
FácilConstruyamos un sistema de inventario de productos que use generadores para iterar eficientemente a través de colecciones grandes sin cargar todo en la memoria de una sola vez. Crearás una clase que almacene productos y proporcione métodos generadores para iterar a través de ellos de distintas maneras.
Organizarás tu código en tres archivos:
Product.php: Crea una claseProductque represente un artículo del inventario. Usa la promoción de constructores para definir un$sku(string) público, un$name(string) público y un$price(float) público.Inventory.php: Crea una claseInventoryque administre una colección de productos y proporcione métodos generadores para iterar a través de ellos. Incluye el archivo Product. La clase debe:- Tener una propiedad de tipo array privada para almacenar los productos
- Tener un método
addProduct(Product $product)que agregue un producto a la colección - Tener un método generador
allProducts(): Generatorque produzca cada producto de uno en uno - Tener un método generador
expensiveProducts(float $minPrice): Generatorque produzca únicamente los productos cuyo precio sea mayor o igual que el mínimo indicado, produciendo pares clave-valor donde la clave sea el SKU y el valor sea el producto
main.php: Incluye el archivo Inventory. Recibirás una entrada: un umbral de precio mínimo.Crea un
Inventoryy agrega estos tres productos:- SKU:
"A001", Nombre:"Laptop", Precio:999.99 - SKU:
"A002", Nombre:"Mouse", Precio:29.99 - SKU:
"A003", Nombre:"Monitor", Precio:349.99
Primero, itera a través de
allProducts()e imprime el nombre de cada producto en su propia línea.Después imprime una línea vacía y, a continuación, itera a través de
expensiveProducts()usando el umbral de entrada. Para cada producto caro, imprime"[sku]: [name] - $[price]"en su propia línea. Formatea el precio con exactamente 2 decimales.- SKU:
Este desafío demuestra cómo los generadores permiten iterar de forma perezosa a través de colecciones: cada producto se produce únicamente cuando se solicita, lo que hace que este enfoque sea eficiente en cuanto a memoria para inventarios grandes. La producción de pares clave-valor en expensiveProducts() muestra cómo los generadores pueden proporcionar datos de iteración estructurados.
Pruébalo tú mismo
<?php
require_once 'Inventory.php';
// Leer entrada
$minPrice = floatval(trim(fgets(STDIN)));
// TODO: Crea una instancia de Inventory
// TODO: Añade tres productos:
// SKU: "A001", Nombre: "Laptop", Precio: 999.99
// SKU: "A002", Nombre: "Mouse", Precio: 29.99
// SKU: "A003", Nombre: "Monitor", Precio: 349.99
// TODO: Itera a través de allProducts() e imprime el nombre de cada producto
// TODO: Imprime una línea vacía
// TODO: Itera a través de expensiveProducts() usando $minPrice
// Para cada producto caro, imprime "[sku]: [name] - $[price]"
// Formatea el precio con exactamente 2 decimales
?>Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de la POO
Archivos externosIntroducción a la POOClases frente a objetosLa palabra clave $thisMétodosPropiedadesConstructor (__construct)Destructor (__destruct)Repaso: calculadora simple4Herencia
Herencia básicaLa palabra clave parent::Sobrescritura de métodosLa palabra clave finalClases abstractasRepaso - Jerarquía de empleados7Encapsulación
Público, protegido, privadoModificadores de acceso en profundidadGetters y settersOcultación de la informaciónPromoción del constructor (8.0)Repaso: sistema de registros de estudiantes10Conceptos avanzados de OOP
Composición vs. herenciaInyección de dependenciasClases anónimasEnums (PHP 8.1)Fibras (PHP 8.1)Análisis profundo de la clonación de objetosGeneradores e iteradores13Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository2Namespaces y carga automática
Introducción a los NamespacesLa palabra clave useEstándar de carga automática PSR-4Autoloader de ComposerRepaso: proyecto organizado5Interfaces y contratos
Introducción a las interfacesImplementación de interfacesImplementación de múltiples interfacesInterfaces vs. clase abstractaTipado con interfacesRepaso - Calculadora de formas8Métodos mágicos
Introducción a los métodos mágicos__toString y __debugInfo__get, __set, __isset, __unset__call y __callStatic__clone y clonación de objetos__serialize y __unserializeRepaso: colección personalizada3Propiedades de clase
Propiedades de instancia vs. estáticasConstantes en clasesMétodos y propiedades estáticosPropiedades privadas y protegidasPropiedades de solo lectura (PHP 8.1)Repaso: gestor de cuentas bancarias6Polimorfismo
Repaso de la sobrescritura de métodosPolimorfismo mediante interfacesAnotaciones de tipo y tipos uniónEnlace estático tardíoRepaso: procesador de pagos9Traits
Introducción a los TraitsUso de múltiples TraitsResolución de conflictos entre TraitsMétodos abstractos en TraitsTraits frente a la herencia12Patrones de diseño, parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón StrategyPractica por tu cuenta: Compilador de PHP online