Menu
Coddy logo textTech

ジェネレーターとイテレーター

CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 62/91。

ジェネレーターは、完全なIteratorインターフェースを実装せずにイテレーターを作成するための簡単な方法を提供します。ジェネレーター関数は、値を一度に1つずつ生成するためにreturnの代わりにyieldを使用し、各値の間で実行を一時停止します。

数値のシーケンスを生成する基本的なジェネレーターを次に示します。

<?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

最大の利点はメモリ効率です。メモリ上に完全な array を構築する代わりに、generator は必要に応じて値を生成します。これは大規模なデータセットを扱う場合に非常に重要です。100万件のレコードを処理する generator も、10件を処理する generator と同じメモリしか使用しません。

ジェネレーターはキーと値のペアを生成することもできます:

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

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

出力:

admin: Alice
editor: Bob

foreachで自分のclassを反復可能にするには、IteratorインターフェースをImplementします。ただし、ほとんどの場合、generatorを使うほうが、ボイラープレートコードが少なく、よりすっきりした解決策になります。特にOOPでは、すべてを一度にメモリへ読み込まずに、classが項目のcollectionを公開する必要がある場合に便利です。

challenge icon

チャレンジ

簡単

すべてを一度にメモリへ読み込まず、大規模な collection を効率的に Iterate するために generator を使用する product inventory system を構築しましょう。product を格納し、さまざまな方法で Iterate するための generator method を提供する class を作成します。

コードを3つのファイルに分けて整理します。

  • Product.php: inventory 内の item を表す Product class を Create します。constructor promotion を使用して、public $sku(string)、public $name(string)、public $price(float)を define します。
  • Inventory.php: product の collection を管理し、それらを Iterate する generator method を提供する Inventory class を Create します。Product file を Include します。class には次の内容を含めます。
    • product を格納する private array property を持つ
    • product を collection に Add する addProduct(Product $product) method を持つ
    • 各 product を one at a time で yield する allProducts(): Generator generator method を持つ
    • 指定された minimum 以上の price を持つ product only を yield し、key が SKU、value が product となる key-value pair を yield する expensiveProducts(float $minPrice): Generator generator method を持つ
  • main.php: Inventory file を Include します。input は1つ、つまり minimum price threshold を受け取ります。

    Inventory を Create し、次の3つの product を Add します。

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

    まず、allProducts() を Iterate し、各 product の name をそれぞれ別の行に print します。

    次に empty line を print し、その後、input threshold を使用して expensiveProducts() を Iterate します。expensive product ごとに、"[sku]: [name] - $[price]" をそれぞれ別の行に print します。price は小数点以下 exactly 2桁で Format します。

この challenge は、generator によって collection を lazy に Iterate できる仕組みを示しています。各 product は要求されたときにのみ yield されるため、大規模な inventory でこの approach は memory-efficient です。expensiveProducts() における key-value の yield は、generator が structured iteration data を提供できる方法を示しています。

自分で試してみよう

<?php

require_once 'Inventory.php';

// 入力を読み取る
$minPrice = floatval(trim(fgets(STDIN)));

// TODO: Inventory のインスタンスを作成する

// TODO: 3つの製品を追加する:
// 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: $minPrice を使用して expensiveProducts() を反復処理する
// 各高価な製品について、"[sku]: [name] - $[price]" を出力する
// 価格を小数点以下ちょうど2桁でフォーマットする

?>
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: PHPオンラインコンパイラ