Page 1
ジェネレータで無限を手玉に取る術
Techniques for controlling infinity using generator
2020-02-11 PHPerKaigi 2020 Day 2
Coconeri Hall, Nerima, Tokyo #phperkaigi
公開日:
by USAMI Kenta @tadsan
に東京都の練馬区立区民・産業プラザ Coconeriホールで開催された『PHPerKaigi 2020』でレギュラートーク(15分)として発表しました。
ジェネレータで無限を手玉に取る術
Techniques for controlling infinity using generator
2020-02-11 PHPerKaigi 2020 Day 2
Coconeri Hall, Nerima, Tokyo #phperkaigi
お前誰よ
うさみけんた (@tadsan)
/ Zonu.EXE
近況
本日のお題
‒ジェネレータで無限を手玉に取る術 PHPerKaigi 2020
https://fortee.jp/phperkaigi-2020/proposal/5b1651a5-0a01-4b49-b338-e332f981bf5d
無限を
手玉に取る
元ねたがある
いますぐ皆さんに
函数プログラミングを 学べという話ではない
(個人的に) 大事なのは
以下のページ
言ってることは よくわからんが
カッコイイ
何が
言いたいのか?
遅延評価
必要になるまで 計算を遅らせる
関数型知識ゼロでも
その神髄の一端を
いとも簡単に扱える 機能がPHPにはある
ここまで言えば
おわかりですね?
ジェネレータ
Generator
ジェネレータ
とは何か?
「PHPでforeachで反復できるものは何でしょう?」配列、オ ブジェクト、Iterator、そして“ジェネレータ”です。ジェネレー タは言語によってコルーチン、semicoroutineやFiberとも呼 ばれるものと同等の言語機能であり、SICPのような計算機科学 の教科書で説明されるストリーム・無限リストを簡潔に実現で きます。foreachループで配列を反復するのは既に作成済みの データを頭から辿っていくだけのものであるのに対して、ジェ ネレータは配列のキーや要素に相当するものを計算によって生 み出すことができるため、省メモリで効率のよい計算が可能で す。このトークではジェネレータの基礎概念と、実際に応用で
きるパターンについて紹介していきます。
‒ジェネレータで無限を手玉に取る術 PHPerKaigi 2020
https://fortee.jp/phperkaigi-2020/proposal/5b1651a5-0a01-4b49-b338-e332f981bf5d
この説明で全部
なのですが…
ざっくり説明 していきます
「PHPでforeachで反復できるものは何でしょう?」 配列、オブジェクト、Iterator、そして“ジェネレータ”
です。
‒ジェネレータで無限を手玉に取る術 PHPerKaigi 2020
https://fortee.jp/phperkaigi-2020/proposal/5b1651a5-0a01-4b49-b338-e332f981bf5d
ジェネレータはforeachできる
配列を代用できる場面がある
全てを置き換えるものではない あくまでforeachで順次実行
するもののみ
その他の反復可能オブジェクト
については今回触れません
iterable擬似型 (PHP 7.1)
擬似型とは実態のある一種類の 型ではなく同じように扱える複 数の型とクラスをひとまとめに
した便宜上のもの
iterable = foreachできる値
おまけ: 擬似型
前述したように特定の型ではな いが、型宣言(引数・戻り値)の
型として記述できる
callable擬似型
関数として呼び出せる値
おまけ: iterableと型定義
DocCommentに型を書くとき
キーと値の型を書ける
や
iterable<int,string>
のように
Generator<int,string>
PhpStormは未対応なので
で代用…
iterable|string[]
おまけ: iterableと型定義
iterableの型をちゃんと書いておくと
foreachの中身も静的解析できる
PHPStanは書かないと叱ってくれる JetBrainsさん早くPhpStorm対応し て(deep-assocプラグインで補完可能) 詳細は「2018年のPHPDoc事情とPSR-5」に書きました
置換できる可能性がある
<?php
どこかから取得してきた リスト
// ID
$target_ids = [85490, 54352, 34242, 45090];
foreach ($target_ids as $id) {
shori($id);
}
置換できる可能性がある
<?php
ジェネレータのイメージ
//
$target_ids = function () { ... };
foreach ($target_ids as $id) {
shori($id);
}
ジェネレータは言語によってコルーチン、
semicoroutineやFiberとも呼ばれるものと同等の言
語機能であり、
‒ジェネレータで無限を手玉に取る術 PHPerKaigi 2020
https://fortee.jp/phperkaigi-2020/proposal/5b1651a5-0a01-4b49-b338-e332f981bf5d
用語 (呼ばれかた)
言語によって呼びかたや機能が
若干違います
JavaScriptやPythonのジェ ネレータはPHPと似ています
RubyにはEnumeratorと Fiberというものがあります
コルーチン (coroutine)
ジェネレータは古典的なコルー チンと同等のものですが、現代 でコルーチンと呼ばれているも のは意味がかなり拡大している
ので注意が必要です
SICPのような計算機科学の教科書で説明されるスト
リーム・無限リストを簡潔に実現できます。
‒ジェネレータで無限を手玉に取る術 PHPerKaigi 2020
https://fortee.jp/phperkaigi-2020/proposal/5b1651a5-0a01-4b49-b338-e332f981bf5d
SICP
計算機プログラムの
構造と解釈
‒計算機プログラムの構造と解釈 第2版
https://www.amazon.co.jp/dp/4798135984
Webで無料で
も読めます
‒計算機プログラムの構造と解釈 第2版
https://sicp.iijlab.net/fulltext/
‒アスペ日記 非公式PDF版SICP・新訳
https://takeda25.hatenablog.jp/entry/20151030/1446174031
計算機科学の入門 書という扱いだが、 簡単な本ではない
この本の序盤 2章あたりに
書いてあること
ペア
(二値の組を持つオブ
があれば様々
ジェクト)
なデータ構造が作れる
ペアの片方を関数で
遅延させることで
ストリーム(遅延リス
ト)というものが作れる
ストリーム (遅延リスト)
配列・オブジェクトのようなも のとクロージャがあれば実現で
きる
PHPで実装してみた
https://github.com/zonuexe/phperkaigi-generator/blob/master/
stream.php
ストリームでできる ことはジェネレータ
で表現できる
foreachループで配列を反復するのは既に作成済みの データを頭から辿っていくだけのものであるのに対し
て、ジェネレータは配列のキーや要素に相当するものを 計算によって生み出すことができるため、省メモリで効
率のよい計算が可能です。
‒ジェネレータで無限を手玉に取る術 PHPerKaigi 2020
https://fortee.jp/phperkaigi-2020/proposal/5b1651a5-0a01-4b49-b338-e332f981bf5d
ということで、 ジェネレータを
説明します
ジェネレータの
書きかた
ジェネレータ=特別な関数
関数定義(メソッド・クロー
ジャを含む)の中でyieldと書く
とジェネレータになる
yieldに渡した値はforeachの
値として受け渡される
PHPUnit dataProvider
テストケースに必要なパラメー
タを生成するメソッド
配列を返すように作るのが一般 的だが、実はジェネレータでも
作れる
基本的なジェネレータ
<?php
どこかから取得してきた リスト
// ID
f
$target_ids = [85490, 54352, 34242, 45090];
foreach ($target_ids as $id) {
shori($id);
}
基本的なジェネレータ
<?php
どこかから取得してきた リスト
// ID
f
$target_ids = [85490, 54352, 34242, 45090];
foreach ($target_ids as $id) {
shori($id);
}