Menu
Coddy logo textTech

Generatory i iteratory

Część sekcji Programowanie obiektowe ścieżki PHP w Coddy. Lekcja 62 z 91.

Generatory zapewniają prosty sposób tworzenia iteratorów bez implementowania pełnego interfejsu Iterator. Funkcja generatora używa yield zamiast return, aby zwracać wartości pojedynczo, wstrzymując wykonanie między kolejnymi wartościami.

Oto podstawowy generator, który tworzy sekwencję liczb:

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

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

Wynik:

1
2
3

Kluczową zaletą jest oszczędność pamięci. Zamiast tworzyć całą tablicę w pamięci, generatory wytwarzają wartości na żądanie. Ma to kluczowe znaczenie podczas pracy z dużymi zbiorami danych — generator przetwarzający milion rekordów zużywa tyle samo pamięci co generator przetwarzający dziesięć rekordów.

Generatory mogą również zwracać pary klucz-wartość:

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

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

Wynik:

admin: Alice
editor: Bob

Aby umożliwić iterowanie po własnych klasach za pomocą foreach, zaimplementuj interfejs Iterator. Jednak w większości przypadków generatory oferują prostsze rozwiązanie, wymagające mniej kodu pomocniczego. Są szczególnie przydatne w programowaniu obiektowym, gdy klasa musi udostępniać kolekcję elementów bez jednoczesnego ładowania wszystkiego do pamięci.

challenge icon

Wyzwanie

Łatwy

Stwórzmy system zarządzania zapasami produktów, który wykorzystuje generatory do wydajnego iterowania po dużych kolekcjach bez ładowania wszystkiego naraz do pamięci. Utworzysz klasę, która przechowuje produkty i udostępnia metody generatorowe do iterowania po nich na różne sposoby.

Rozmieścisz swój kod w trzech plikach:

  • Product.php: Utwórz klasę Product, która reprezentuje produkt w zapasach. Użyj promocji właściwości konstruktora, aby zdefiniować publiczne $sku (string), publiczne $name (string) oraz publiczne $price (float).
  • Inventory.php: Utwórz klasę Inventory, która zarządza kolekcją produktów i udostępnia metody generatorowe do iterowania po nich. Dołącz plik Product. Klasa powinna:
    • Mieć prywatną właściwość typu tablica do przechowywania produktów
    • Mieć metodę addProduct(Product $product), która dodaje produkt do kolekcji
    • Mieć metodę generatorową allProducts(): Generator, która zwraca każdy produkt pojedynczo za pomocą yield
    • Mieć metodę generatorową expensiveProducts(float $minPrice): Generator, która zwraca tylko produkty o cenie większej lub równej podanej wartości minimalnej, zwracając pary klucz-wartość, gdzie kluczem jest SKU, a wartością produkt
  • main.php: Dołącz plik Inventory. Otrzymasz jedno wejście: próg ceny minimalnej.

    Utwórz obiekt Inventory i dodaj te trzy produkty:

    • SKU: "A001", nazwa: "Laptop", cena: 999.99
    • SKU: "A002", nazwa: "Mouse", cena: 29.99
    • SKU: "A003", nazwa: "Monitor", cena: 349.99

    Najpierw iteruj przez allProducts() i wypisz nazwę każdego produktu w osobnym wierszu.

    Następnie wypisz pusty wiersz, po czym iteruj przez expensiveProducts(), używając progu z wejścia. Dla każdego drogiego produktu wypisz "[sku]: [name] - $[price]" w osobnym wierszu. Sformatuj cenę z dokładnością do 2 miejsc po przecinku.

To wyzwanie pokazuje, jak generatory umożliwiają leniwe iterowanie po kolekcjach: każdy produkt jest zwracany dopiero na żądanie, dzięki czemu takie podejście jest wydajne pod względem pamięci w przypadku dużych zapasów. Zwracanie par klucz-wartość w expensiveProducts() pokazuje, jak generatory mogą dostarczać uporządkowane dane iteracji.

Spróbuj swoich sił

<?php

require_once 'Inventory.php';

// Odczytaj dane wejściowe
$minPrice = floatval(trim(fgets(STDIN)));

// TODO: Utwórz instancję Inventory

// TODO: Dodaj trzy produkty:
// SKU: "A001", Nazwa: "Laptop", Cena: 999.99
// SKU: "A002", Nazwa: "Mouse", Cena: 29.99
// SKU: "A003", Nazwa: "Monitor", Cena: 349.99

// TODO: Przejdź przez allProducts() i wypisz nazwę każdego produktu

// TODO: Wypisz pustą linię

// TODO: Przejdź przez expensiveProducts(), używając $minPrice
// Dla każdego drogiego produktu wypisz "[sku]: [name] - $[price]"
// Sformatuj cenę z dokładnie 2 miejscami po przecinku

?>
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator PHP online