Menu
Coddy logo textTech

Generatoren & Iteratoren

Teil des Abschnitts Objektorientierte Programmierung der PHP-Journey von Coddy — Lektion 62 von 91.

Generatoren bieten eine einfache Möglichkeit, Iteratoren zu erstellen, ohne das vollständige Iterator-Interface zu implementieren. Eine Generator-Funktion verwendet yield anstelle von return, um Werte nacheinander zu erzeugen, wobei die Ausführung zwischen den einzelnen Werten angehalten wird.

Hier ist ein einfacher Generator, der eine Zahlenfolge erzeugt:

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

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

Ausgabe:

1
2
3

Der entscheidende Vorteil ist die Speichereffizienz. Anstatt ein gesamtes Array im Speicher aufzubauen, erzeugen Generatoren Werte nach Bedarf. Dies ist entscheidend bei der Arbeit mit großen Datensätzen – ein Generator, der eine Million Datensätze verarbeitet, verbraucht denselben Speicher wie einer, der zehn verarbeitet.

Generatoren können auch Schlüssel-Wert-Paare liefern:

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

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

Ausgabe:

admin: Alice
editor: Bob

Um Ihre eigenen Klassen mit foreach iterierbar zu machen, implementieren Sie das Iterator-Interface. In den meisten Fällen bieten Generatoren jedoch eine sauberere Lösung mit weniger Boilerplate-Code. Sie sind besonders nützlich in der OOP, wenn eine Klasse eine Sammlung von Elementen bereitstellen muss, ohne alles auf einmal in den Speicher zu laden.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Produktinventarsystem aufbauen, das Generatoren verwendet, um effizient durch große Sammlungen zu iterieren, ohne alles auf einmal in den Speicher zu laden. Sie werden eine Klasse erstellen, die Produkte speichert und Generator-Methoden bereitstellt, um auf verschiedene Arten durch sie zu iterieren.

Sie werden Ihren Code über drei Dateien organisieren:

  • Product.php — Erstellen Sie eine Product-Klasse, die einen Artikel im Inventar darstellt. Verwenden Sie Constructor Promotion, um ein öffentliches $sku (string), ein öffentliches $name (string) und ein öffentliches $price (float) zu definieren.
  • Inventory.php — Erstellen Sie eine Inventory-Klasse, die eine Sammlung von Produkten verwaltet und Generator-Methoden bereitstellt, um durch diese zu iterieren. Binden Sie die Product-Datei ein. Die Klasse sollte:
    • Eine private Array-Eigenschaft haben, um Produkte zu speichern
    • Eine Methode addProduct(Product $product) haben, die ein Produkt zur Sammlung hinzufügt
    • Eine Generator-Methode allProducts(): Generator haben, die jedes Produkt einzeln nacheinander ausgibt (yield)
    • Eine Generator-Methode expensiveProducts(float $minPrice): Generator haben, die nur Produkte mit einem Preis größer oder gleich dem angegebenen Minimum ausgibt, wobei Key-Value-Paare ausgegeben werden, bei denen der Key die SKU und der Value das Produkt ist
  • main.php — Binden Sie die Inventory-Datei ein. Sie erhalten eine Eingabe: einen Mindestpreisschwellenwert.

    Erstellen Sie ein Inventory und fügen Sie diese drei Produkte hinzu:

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

    Iterieren Sie zuerst durch allProducts() und geben Sie den Namen jedes Produkts in einer eigenen Zeile aus.

    Geben Sie dann eine Leerzeile aus, gefolgt von der Iteration durch expensiveProducts() unter Verwendung des eingegebenen Schwellenwerts. Geben Sie für jedes teure Produkt "[sku]: [name] - $[price]" in einer eigenen Zeile aus. Formatieren Sie den Preis mit genau 2 Dezimalstellen.

Diese Herausforderung zeigt, wie Generatoren es Ihnen ermöglichen, Sammlungen verzögert (lazy) zu durchlaufen — jedes Produkt wird erst dann ausgegeben, wenn es angefordert wird, was diesen Ansatz für große Inventare speichereffizient macht. Die Key-Value-Ausgabe in expensiveProducts() zeigt, wie Generatoren strukturierte Iterationsdaten bereitstellen können.

Spickzettel

Generatoren bieten eine speichereffiziente Möglichkeit, Iteratoren zu erstellen, indem sie yield anstelle von return verwenden. Sie erzeugen Werte nacheinander und unterbrechen die Ausführung zwischen den einzelnen Werten.

Grundlegende Generator-Funktion:

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

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

Generatoren können Schlüssel-Wert-Paare liefern:

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

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

Der Hauptvorteil ist die Speichereffizienz – Generatoren erzeugen Werte bei Bedarf, anstatt ganze Arrays im Speicher aufzubauen. Dies ist besonders nützlich bei der Arbeit mit großen Datensätzen oder Sammlungen in OOP-Kontexten.

Probier es selbst

<?php

require_once 'Inventory.php';

// Eingabe lesen
$minPrice = floatval(trim(fgets(STDIN)));

// TODO: Eine Inventory-Instanz erstellen

// TODO: Drei Produkte hinzufügen:
// SKU: "A001", Name: "Laptop", Preis: 999.99
// SKU: "A002", Name: "Mouse", Preis: 29.99
// SKU: "A003", Name: "Monitor", Preis: 349.99

// TODO: Durch allProducts() iterieren und den Namen jedes Produkts ausgeben

// TODO: Eine Leerzeile ausgeben

// TODO: Durch expensiveProducts() unter Verwendung von $minPrice iterieren
// Für jedes teure Produkt "[sku]: [name] - $[price]" ausgeben
// Preis mit genau 2 Dezimalstellen formatieren

?>
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung