$30 off During Our Annual Pro Sale. View Details »

Qiita Night PHP 2023

Qiita Night PHP 2023

登壇資料です

ふわせぐ

January 26, 2023
Tweet

More Decks by ふわせぐ

Other Decks in Programming

Transcript

  1. ൓෮ॲཧͱ JUFSBCMFͰ༇շͳ஥ؒͨͪ
    Hirosugu Takeshita @fuwasegu

    View Slide

  2. • 竹下 拓秀 / ふわせぐ (@fuwasegu)
    • 株式会社ゆめみ / 21卒
    – コーポレートエンジニア
    • PHP / Laravel がメイン
    – Svelte(JS / TS),Rust Go 勉強中
    • 長崎県出身 / 愛知県在住
    • !(3 歳)の父
    2
    自己紹介
    Qiita Night 〜PHP〜

    View Slide

  3. 宣伝
    3
    Qiita Night 〜PHP〜
    • mpyw さんと二人で Laravel の
    ソースコードを読む会
    • 隔週で YouTube Live で配信中
    • バックナンバー
    • Model の with
    • Model の scope
    • SoftDelete
    • サービスコンテナ
    • ルートモデルバインディング
    • ExceptionHandler
    次回: 2023-01-30 14:00:00

    View Slide

  4. • PHP での反復処理について
    • Generator の紹介
    4
    今日話すこと
    Qiita Night 〜PHP〜

    View Slide

  5. 5
    7 分しかないのでめっちゃ突っ走ります
    (後日 Qiita に記事出すので詳しくはそちらで)
    Qiita Night 〜PHP〜

    View Slide

  6. 反復処理(Iteration)ってどんなもの?
    Qiita Night 〜PHP〜 6
    反復可能な値の各要素に対して,同じ処理を順番に行うこと

    View Slide

  7. • オブジェクト
    • iterable 型
    7
    PHP で反復可能な値になりうるもの
    Qiita Night 〜PHP〜

    View Slide

  8. • オブジェクト
    8
    PHP で反復可能な値になりうるもの
    Qiita Night 〜PHP〜
    アクセス権限があるプロパティは,
    foreach で取り出せる
    (private なものは取り出せない)

    View Slide

  9. • iterable 型
    9
    PHP で反復可能な値になりうるもの
    Qiita Night 〜PHP〜
    今日の本題はこっち

    View Slide

  10. • iterable = (Traversable | array)
    – 型のエイリアス
    – PHP7.1 〜 PHP8.0 では疑似型
    – それ以前は未定義
    • foreach で反復処理ができるもの
    • iterator_to_array() に渡すと array になるもの
    – iterator_to_array() の引数 Traversable | array になったのは
    PHP8.2 から
    – それまでは Traversable のみだった
    10
    iterable とは
    Qiita Night 〜PHP〜

    View Slide

  11. 11
    Qiita Night 〜PHP〜
    iterable
    array
    Traversable

    View Slide

  12. 12
    Qiita Night 〜PHP〜
    iterable
    array
    Traversable
    IteratorAggregate Iterator
    Generator
    extends
    extends
    extends
    implements

    View Slide

  13. 13
    Qiita Night 〜PHP〜
    iterable
    IteratorAggregate Iterator
    Generator
    extends
    extends
    extends
    implements
    時間がないので説明は割愛

    View Slide

  14. Iterator とは
    • 反復処理中の状態を持つオブジェクト自身を
    定義するinterface
    – 現在の要素を取得
    – 現在のキーを取得
    – 次の要素に進む
    – 最初の要素に巻き戻し
    – 現在位置が有効かどうか
    取得
    Qiita Night 〜PHP〜 14

    View Slide

  15. IteratorAggregate とは
    • Iterator(Traversable) を作る interface
    • 自分自身が要素を持ったり状態を管理するわけ
    ではない
    Qiita Night 〜PHP〜 15
    • getIterator() は新しいイテレータ
    を返す
    • foreach に IteratorAggregate を渡
    すと勝手に getIterator() が呼ばれ

    ポイント

    View Slide

  16. Generator とは
    • イテレータが簡単に作れるやつ
    – 本来 Iterator を自前で作る場合,インタフェースに定
    義されたメソッドをそれぞれ実装する必要がある
    – Generator は yield するだけで良い
    • 遅延評価される(= 省メモリ)
    – 値(演算結果)が必要になったときに初めて評価
    されるということ
    Qiita Night 〜PHP〜 16

    View Slide

  17. 17
    ジェネレーター構文の例
    Qiita Night 〜PHP〜

    View Slide

  18. 18
    ジェネレーター構文の例
    Qiita Night 〜PHP〜
    まだ何が嬉しいか分からん!

    View Slide

  19. 19
    もうちょっと
    具体的なユースケースを考えます
    Qiita Night 〜PHP〜

    View Slide

  20. 仮想の要件
    • 大容量の CSV ファイルを読み込む
    – ID,姓,名,生年月日,郵便番号,電話番号,メールアドレ
    ス,性別
    • フィルターしてデータを抜き出す
    – 名字が山田
    – 二十歳以上
    – 男
    – 頭から 3 件取得する
    Qiita Night 〜PHP〜 20

    View Slide

  21. 愚直に実装してみる(方針)
    • 一気には読み込めないので一行ずつ読む
    – fopen() して fgetcsv() しながらループ
    • ループの中で 条件に合致しなかったら読み
    飛ばす
    • カウンタを作っておいてインクリメントしていく
    – 必要数集まったら break
    Qiita Night 〜PHP〜 21

    View Slide

  22. 22
    Qiita Night 〜PHP〜
    条件の数だけ if を並べる 1個の if にまとめる
    愚直に実装してみる(実装)
    文字が小さいのは許して

    View Slide

  23. 23
    Qiita Night 〜PHP〜
    愚直に実装してみる(実装)

    View Slide

  24. 24
    メソッドに分割してみる
    Qiita Night 〜PHP〜
    年齢でフィルター
    名前でフィルター
    性別でフィルター
    指定要素数取り出し

    View Slide

  25. • 一見見通しが良くなったが,前もって
    CSV を全部読み込む必要があり激重
    • 毎回 foreach で全探索するので激重
    25
    分割したメソッドで組み立て
    Qiita Night 〜PHP〜
    CSV が大きいとメモリが食いつぶされます

    View Slide

  26. 26
    Generator を使って書き直す
    Qiita Night 〜PHP〜
    年齢でフィルター
    性別でフィルター
    名前でフィルター
    指定要素数取り出し
    CSV 読み込み

    View Slide

  27. 27
    Generator を使って書き直す
    Qiita Night 〜PHP〜
    今回は Generator 関数を用いましたが,IteratorAggregate を
    使ってクラスごとに分割する方法もあります
    (Qiita で解説します!)

    View Slide

  28. • さっきと見た目は変わらない
    • 全て Generator なので CSV を一気に読み
    込む必要も無くなった
    28
    分割したメソッドで組み立て
    Qiita Night 〜PHP〜
    CSV がどれだけ大きくても動作します

    View Slide

  29. • Generator を使わない場合
    – Fatal error: Allowed memory size of 134217728 bytes
    exhausted (tried to allocate 20480 bytes)
    • Generator を使った場合
    – 2936 バイトで正常に終了
    29
    実験
    Qiita Night 〜PHP〜
    • 100 万行の CSV(77.2 MB)を入力
    • CSV の読み込み 〜 フィルターにかかる使用メモリを計測
    結果

    View Slide

  30. • Generator は yield が呼ばれるたびに値を生成する
    – Generator でない場合要素分のメモリが必要
    – Generator が生成した値は消費されるので古いものは破棄される
    – つまり巻き戻せない(単一方向の Iterator)
    • Generator は値が必要になったときに初めて評価される
    – 並列で並べた時,Generator でない場合毎回全部ループして都度
    全要素評価する
    – Generator の場合,一行読み込むたびに次の処理に移れる
    30
    なぜ Generator は省メモリなのか
    Qiita Night 〜PHP〜
    メモリの使用量が要素数に比例しないので,無限長のリストを扱える

    View Slide

  31. 31
    実際の処理を見てみよう
    Qiita Night 〜PHP〜
    6, 2, 10, 9, 3, 12
    読み取りたいデータ
    $data = read_data();
    // 5 以上の整数に絞る
    $result = filter_1($data);
    // 偶数に絞る
    $result = filter_2($result);
    // 頭から 2 つだけとる
    $result = take($result, 2);
    foreach ($result as $item) {
    echo $item;
    }
    擬似コード

    View Slide

  32. 32
    Generator を使わない場合
    Qiita Night 〜PHP〜
    全部読み込み
    全探索で絞り込み
    全探索で絞り込み
    早期リターン
    出力
    $data = read_data();
    // 5 以上の整数に絞る
    $result = filter_1($data);
    // 偶数に絞る
    $result = filter_2($result);
    // 頭から 2 つだけとる
    $result = take($result, 2);
    foreach ($result as $item) {
    echo $item;
    }

    View Slide

  33. 33
    Generator を使わない場合
    Qiita Night 〜PHP〜
    6 2 10 9 3 12
    read_data 1 2 3 4 5 6
    filter_1 7 8 9 10 11 12
    filter_2 13 14 15 16
    take 17 18
    foreach 19 20
    データ
    処理
    黒の数字は読み取り順 処理が軸になる

    View Slide

  34. 34
    Generator を使う場合
    Qiita Night 〜PHP〜
    1 要素目
    2 要素目
    3 要素目
    $data = read_data();
    // 5 以上の整数に絞る
    $result = filter_1($data);
    // 偶数に絞る
    $result = filter_2($result);
    // 頭から 2 つだけとる
    $result = take($result, 2);
    foreach ($result as $item) {
    echo $item;
    }

    View Slide

  35. 35
    Generator を使う場合
    Qiita Night 〜PHP〜
    6 2 10 9 3 12
    read_data 1 6 8
    filter_1 2 7 9
    filter_2 3 10
    take 4 11
    foreach 5 12
    データ
    処理
    黒の数字は読み取り順 値が軸になる

    View Slide

  36. • Generator を使わない場合
    – 全てのデータを読み込んだ
    – メソッドは呼び出し時に実行された
    – 毎回全要素を参照した
    • take の早期リターンは例外
    • Generator を使った場合
    – 全てのデータは読み込まなかった
    – メソッドは呼び出し時に実行されなかった
    • 要素ごとに遅延実行された
    – 演算対象の要素のみ参照した
    36
    結果
    Qiita Night 〜PHP〜
    $data = read_data();
    // 5 以上の整数に絞る
    $result = filter_1($data);
    // 偶数に絞る
    $result = filter_2($result);
    // 頭から 2 つだけとる
    $result = take($result, 2);
    foreach ($result as $item) {
    echo $item;
    }

    View Slide

  37. 37
    Qiita Night 〜PHP〜
    Generator はいいぞ!

    View Slide