Menu
Coddy logo textTech

Generadores e Iteradores

Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy — lección 62 de 91.

Generadores proporcionan una forma sencilla de crear iteradores sin implementar la interfaz Iterator completa. Una función generadora utiliza yield en lugar de return para producir valores uno a la vez, 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";
}

Resultado:

1
2
3

La 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 cuando se trabaja con grandes conjuntos de datos: un generador que procesa un millón de registros utiliza la misma memoria que uno que procesa diez.

Los generadores también pueden emitir 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: Bob

Para hacer que sus propias clases sean iterables con foreach, implemente 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 particularmente útiles en la POO cuando una clase necesita exponer una colección de elementos sin cargar todo en la memoria a la vez.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de inventario de productos que utiliza generadores para iterar eficientemente a través de grandes colecciones sin cargar todo en la memoria a la vez. Crearás una clase que almacena productos y proporciona métodos generadores para iterar a través de ellos de diferentes maneras.

Organizarás tu código en tres archivos:

  • Product.php — Crea una clase Product que represente un artículo en el inventario. Usa la promoción de constructor para definir un $sku (string) público, un $name (string) público y un $price (float) público.
  • Inventory.php — Crea una clase Inventory que gestione 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 array privada para almacenar productos
    • Tener un método addProduct(Product $product) que añada un producto a la colección
    • Tener un método generador allProducts(): Generator que devuelva (yield) cada producto uno a la vez
    • Tener un método generador expensiveProducts(float $minPrice): Generator que devuelva (yield) solo los productos con un precio mayor o igual al mínimo dado, devolviendo pares clave-valor donde la clave es el SKU y el valor es el producto
  • main.php — Incluye el archivo Inventory. Recibirás una entrada: un umbral de precio mínimo.

    Crea un Inventory y añade estos tres productos:

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

    Primero, itera a través de allProducts() e imprime el nombre de cada producto en su propia línea.

    Luego imprime una línea vacía, seguida de la iteración a través de expensiveProducts() utilizando el umbral de entrada. Para cada producto costoso, imprime "[sku]: [name] - $[price]" en su propia línea. Formatea el precio con exactamente 2 decimales.

Este desafío demuestra cómo los generadores te permiten iterar a través de colecciones de forma perezosa (lazy) — cada producto se genera solo cuando se solicita, lo que hace que este enfoque sea eficiente en memoria para inventarios grandes. La generación de pares clave-valor en expensiveProducts() muestra cómo los generadores pueden proporcionar datos de iteración estructurados.

Hoja de referencia

Los generadores proporcionan una forma eficiente en memoria de crear iteradores usando yield en lugar de return. Producen valores uno a la vez, pausando la ejecución entre cada valor.

Función generadora básica:

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

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

Los generadores pueden emitir pares clave-valor:

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

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

La principal ventaja es la eficiencia de memoria: los generadores producen valores bajo demanda en lugar de construir arrays completos en memoria. Esto es especialmente útil cuando se trabaja con grandes conjuntos de datos o colecciones en contextos de POO.

Pruébalo tú mismo

<?php

require_once 'Inventory.php';

// Leer la entrada
$minPrice = floatval(trim(fgets(STDIN)));

// TODO: Crear una instancia de Inventory

// TODO: Agregar tres productos:
// SKU: "A001", Name: "Laptop", Price: 999.99
// SKU: "A002", Name: "Mouse", Price: 29.99
// SKU: "A003", Name: "Monitor", Price: 349.99

// TODO: Iterar a través de allProducts() e imprimir el nombre de cada producto

// TODO: Imprimir una línea vacía

// TODO: Iterar a través de expensiveProducts() usando $minPrice
// Para cada producto costoso, imprimir "[sku]: [name] - $[price]"
// Formatear el precio con exactamente 2 decimales

?>
quiz iconPonte a prueba

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