ジェネレータで無限を手玉に取る術

公開日:

東京都練馬区立区民・産業プラザ Coconeriホールで開催された『PHPerKaigi 2020』でレギュラートーク(15分)として発表しました。

Download PDF

スライドテキスト

Page 1

ジェネレータで無限を手玉に取る術

Techniques for controlling infinity using generator

2020-02-11 PHPerKaigi 2020 Day 2
Coconeri Hall, Nerima, Tokyo #phperkaigi

Page 2

お前誰よ
うさみけんた (@tadsan)

/ Zonu.EXE

  • GitHub/Packagistでは id: zonuexe
    • ピクシブ株式会社 pixiv運営本部
  • Emacs Lisper, PHPer, Rubyist
  • Emacs PHP Modeのメンテナ引き継ぎました
    • 好きなリスプはEmacs Lispです
    • Qiitaに記事を書いたり変なコメントしてるよ

Page 3

Page 4

Page 5

Page 6

近況

Page 7

Page 8

本日のお題

Page 9

‒ジェネレータで無限を手玉に取る術 PHPerKaigi 2020

https://fortee.jp/phperkaigi-2020/proposal/5b1651a5-0a01-4b49-b338-e332f981bf5d

Page 10

無限を
手玉に取る

Page 11

元ねたがある

Page 12

Page 13

いますぐ皆さんに
函数プログラミングを 学べという話ではない

Page 14

(個人的に) 大事なのは
以下のページ

Page 15

Page 16

Page 17

Page 18

言ってることは よくわからんが
カッコイイ

Page 19

何が
言いたいのか?

Page 20

遅延評価
必要になるまで 計算を遅らせる

Page 21

関数型知識ゼロでも
その神髄の一端を
いとも簡単に扱える 機能がPHPにはある

Page 22

ここまで言えば
おわかりですね?

Page 23

ジェネレータ

Generator

Page 24

ジェネレータ
とは何か?

Page 25

「PHPでforeachで反復できるものは何でしょう?」配列、オ ブジェクト、Iterator、そして“ジェネレータ”です。ジェネレー タは言語によってコルーチン、semicoroutineやFiberとも呼 ばれるものと同等の言語機能であり、SICPのような計算機科学 の教科書で説明されるストリーム・無限リストを簡潔に実現で きます。foreachループで配列を反復するのは既に作成済みの データを頭から辿っていくだけのものであるのに対して、ジェ ネレータは配列のキーや要素に相当するものを計算によって生 み出すことができるため、省メモリで効率のよい計算が可能で す。このトークではジェネレータの基礎概念と、実際に応用で
きるパターンについて紹介していきます。

‒ジェネレータで無限を手玉に取る術 PHPerKaigi 2020

https://fortee.jp/phperkaigi-2020/proposal/5b1651a5-0a01-4b49-b338-e332f981bf5d

Page 26

この説明で全部
なのですが…

Page 27

ざっくり説明 していきます

Page 28

「PHPでforeachで反復できるものは何でしょう?」 配列、オブジェクト、Iterator、そして“ジェネレータ”
です。

‒ジェネレータで無限を手玉に取る術 PHPerKaigi 2020

https://fortee.jp/phperkaigi-2020/proposal/5b1651a5-0a01-4b49-b338-e332f981bf5d

Page 29

ジェネレータはforeachできる

配列を代用できる場面がある
全てを置き換えるものではない あくまでforeachで順次実行
するもののみ
その他の反復可能オブジェクト
については今回触れません

Page 30

iterable擬似型 (PHP 7.1)

擬似型とは実態のある一種類の 型ではなく同じように扱える複 数の型とクラスをひとまとめに
した便宜上のもの
iterable = foreachできる値

Page 31

おまけ: 擬似型

前述したように特定の型ではな いが、型宣言(引数・戻り値)の
型として記述できる
callable擬似型
関数として呼び出せる値

Page 32

おまけ: iterableと型定義

DocCommentに型を書くとき
キーと値の型を書ける

iterable<int,string>

のように

Generator<int,string>
PhpStormは未対応なので

で代用…

iterable|string[]

Page 33

おまけ: iterableと型定義

iterableの型をちゃんと書いておくと
foreachの中身も静的解析できる
PHPStanは書かないと叱ってくれる JetBrainsさん早くPhpStorm対応し て(deep-assocプラグインで補完可能) 詳細は「2018年のPHPDoc事情とPSR-5」に書きました

Page 34

置換できる可能性がある

<?php

どこかから取得してきた リスト

// ID
$target_ids = [85490, 54352, 34242, 45090];

foreach ($target_ids as $id) {
shori($id);
}

Page 35

置換できる可能性がある

<?php

ジェネレータのイメージ

//
$target_ids = function () { ... };

foreach ($target_ids as $id) {
shori($id);
}

Page 36

ジェネレータは言語によってコルーチン、
semicoroutineやFiberとも呼ばれるものと同等の言
語機能であり、

‒ジェネレータで無限を手玉に取る術 PHPerKaigi 2020

https://fortee.jp/phperkaigi-2020/proposal/5b1651a5-0a01-4b49-b338-e332f981bf5d

Page 37

用語 (呼ばれかた)

言語によって呼びかたや機能が
若干違います
JavaScriptやPythonのジェ ネレータはPHPと似ています
RubyにはEnumeratorと Fiberというものがあります

Page 38

コルーチン (coroutine)

ジェネレータは古典的なコルー チンと同等のものですが、現代 でコルーチンと呼ばれているも のは意味がかなり拡大している
ので注意が必要です

Page 39

SICPのような計算機科学の教科書で説明されるスト
リーム・無限リストを簡潔に実現できます。

‒ジェネレータで無限を手玉に取る術 PHPerKaigi 2020

https://fortee.jp/phperkaigi-2020/proposal/5b1651a5-0a01-4b49-b338-e332f981bf5d

Page 40

SICP
計算機プログラムの
構造と解釈

Page 41

‒計算機プログラムの構造と解釈 第2版

https://www.amazon.co.jp/dp/4798135984

Page 42

Webで無料で
も読めます

Page 43

‒計算機プログラムの構造と解釈 第2版

https://sicp.iijlab.net/fulltext/

Page 44

‒アスペ日記 非公式PDF版SICP・新訳

https://takeda25.hatenablog.jp/entry/20151030/1446174031

Page 45

計算機科学の入門 書という扱いだが、 簡単な本ではない

Page 46

この本の序盤 2章あたりに
書いてあること

Page 47

ペア

(二値の組を持つオブ
があれば様々

ジェクト)
なデータ構造が作れる

Page 48

ペアの片方を関数で
遅延させることで
ストリーム(遅延リス
ト)というものが作れる

Page 49

ストリーム (遅延リスト)

配列・オブジェクトのようなも のとクロージャがあれば実現で
きる
PHPで実装してみた

https://github.com/zonuexe/phperkaigi-generator/blob/master/
stream.php

Page 50

ストリームでできる ことはジェネレータ
で表現できる

Page 51

foreachループで配列を反復するのは既に作成済みの データを頭から辿っていくだけのものであるのに対し
て、ジェネレータは配列のキーや要素に相当するものを 計算によって生み出すことができるため、省メモリで効
率のよい計算が可能です。

‒ジェネレータで無限を手玉に取る術 PHPerKaigi 2020

https://fortee.jp/phperkaigi-2020/proposal/5b1651a5-0a01-4b49-b338-e332f981bf5d

Page 52

ということで、 ジェネレータを
説明します

Page 53

ジェネレータの
書きかた

Page 54

ジェネレータ=特別な関数

関数定義(メソッド・クロー
ジャを含む)の中でyieldと書く
とジェネレータになる
yieldに渡した値はforeachの
値として受け渡される

Page 55

PHPUnit dataProvider

テストケースに必要なパラメー
タを生成するメソッド
配列を返すように作るのが一般 的だが、実はジェネレータでも
作れる

Page 56

基本的なジェネレータ

<?php

どこかから取得してきた リスト

// ID 

f
$target_ids = [85490, 54352, 34242, 45090];

foreach ($target_ids as $id) {
shori($id);
}

Page 57

基本的なジェネレータ

<?php

どこかから取得してきた リスト

// ID 

f
$target_ids = [85490, 54352, 34242, 45090];

foreach ($target_ids as $id) {
shori($id);
}