Good Parts of PHP and the UNIX Philosophy

Post on 29-Nov-2014

903 views 0 download

description

PHP の良いパーツとして stream, Iterator, Generator を紹介します。 これらはファイルやコレクションに対して統一的な操作を提供し、小さな関数を組み合わせた関数型プログラミング的なアプローチの助けになります。 また、そういったテクニックの根底にある、UNIX 哲学についても紹介します。

Transcript of Good Parts of PHP and the UNIX Philosophy

Good Parts of PHP and

The UNIX Philosophy@ PHP カンファレンス 2014 フィードバックはこちら

https://joind.in/talk/view/12040

自己紹介

• Twitter: @yuya_takeyama

• GitHub: yuya-takeyama

• PHP / Ruby / Golang

Good Parts•Stream •Iterator •Generator

The UNIX Philosophy

これがUNIXの哲学である。 一つのことを行い、またそれをうまくやるプログラムを書け。 協調して動くプログラムを書け。 標準入出力(テキスト・ストリーム)を扱うプログラムを書け。標準入出力は普遍的インターフェースなのだ。

— M. D. マキルロイ、UNIXの四半世紀

http://ja.wikipedia.org/wiki/UNIX哲学

ひとつのことをうまくやれ

全てのプログラムをフィルタに

部分の総和は全体よりも大きい

対象者•ライブラリを書く人

効用•再利用性の高いコード

•組み合わせられるコード

•が書けるようになる

話さないこと

•並列・並行プログラミング

Stream

$fp = fopen($file, 'r');

Stream•データの流れ

•データを読み込めるかも

•データを書き込めるかも

<?php $source = fopen('./input.txt', 'r'); $dest = fopen('./output.txt', 'w'); if ($source && $dest) { while (($buf = fgets($source, 8192)) !== false) { if (fputs($dest, $buf) === false) { throw new RuntimeException('write error'); } } if (feof($source) === false) { throw new RuntimeException('read error'); } fclose($source); fclose($dest); }

ファイルの読み書き

<?php $source = fopen('./input.txt', 'r'); $dest = fopen('./output.txt', 'w'); if ($source && $dest) { while (($buf = fgets($source, 8192)) !== false) { if (fputs($dest, $buf) === false) { throw new RuntimeException('write error'); } } if (feof($source) === false) { throw new RuntimeException('read error'); } fclose($source); fclose($dest); }

ファイルの読み書き

わりと むずい

何が便利?

Stream wrapper

Stream wrapper• あらゆるものをfopen()可能に!

• 標準インターフェイスとしてのストリーム (または URL)!

• ユーザによる実装も可能

$fp = fopen($url);

file_get_contents($url);

Protocols•file://!

•http(s)://!

•ssh2.sftp://!

•etc…

Formats•phar://!

•zip://!

•rar://!

•etc…

Specials•php://stdin!

•php://memory!

•php://temp!

•etc…

Stream wrapper を

実装する 

http://php.net/manual/ja/class.streamwrapper.php

http://php.net/manual/ja/class.streamwrapper.php

かなり だるい

実例

http://docs.aws.amazon.com/aws-sdk-php/guide/latest/feature-s3-stream-wrapper.html

標準インターフェイス

Stream wrapper x

Symfony 

http://fabien.potencier.org/article/44/php-iterators-and-streams-are-awesome

http://fabien.potencier.org/article/44/php-iterators-and-streams-are-awesome

ひとつのことをうまくやれ

全てのプログラムをフィルタに

部分の総和は全体よりも大きい

http://fabien.potencier.org/article/44/php-iterators-and-streams-are-awesome

Stream Wrapper を利用することで、あらゆるプロトコル・フォーマットを抽象化して操作できる

Stream をフィルタする!プログラムを作って!組み合わせよう

Conclusion of Stream

Iterator

Iterator (in General)•要素の集合

•各要素に順番にアクセス

•リストのような何か

Iterator (in PHP)•Traversable インターフェイス!

•foreach できる何か

Iterator を

実装する 

http://jp1.php.net/manual/ja/class.iterator.php

RangeIterator をつくる 

RangeIterator•最初と最後の値を指定する!

•その値の幅を表すイテレータ!

•各ステップごとの増加数も指定できる

http://qiita.com/yuya_takeyama/items/51fb058ed20d3df8209e

$range = new RangeIterator(1, 10); foreach ($range as $n) { echo $n, PHP_EOL; }

RangeIterator を使う

1 !

!

!

RangeIterator を使う

1 2 !

!

RangeIterator を使う

1 2 3 !

RangeIterator を使う

1 2 3 4

RangeIterator を使う

1 2 3 4 …

RangeIterator を使う

Range といえば…. 

http://php.net/range

range() vs

Rangeiterator

range()

•要素数文の array を予め生成!

•その文メモリを消費

RangeIterator•各ループ時に次の値を計算して生成!

•要素数が増えてもメモリ量は一定!

•無限リストの生成も可能

$range = new RangeIterator(1, INF); foreach ($range as $n) { echo $n, PHP_EOL; }

無限リストを使う

$range = new RangeIterator(1, INF); $range = new LimitIterator($range, 2, 100); foreach ($range as $n) { echo $n, PHP_EOL; }

無限リストを切り取って使う

3 !

!

!

無限リストを切り取って使う

3 4 !

!

無限リストを切り取って使う

3 4 5 !

無限リストを切り取って使う

3 4 5 …

無限リストを切り取って使う

イテレータは

組み合わせられる

イテレータが

別のイテレータを

生成する

ひとつのことをうまくやれ

全てのプログラムをフィルタに

部分の総和は全体よりも大きい

$dir = new RecursiveDirectoryIterator( '/Users/yuya/Music/iTunes/iTunes Media/Music/Aphex Twin' ); $dir = new RecursiveIteratorIterator($dir); $dir = new CallbackFilterIterator($dir, function ($node) { return $node->isFile(); }); $dir = new CallbackFilterIterator($dir, function ($file) { return preg_match('/^01/', $file->getFilename()); }); !foreach ($dir as $file) { echo $file, PHP_EOL; }

イテレータでファイル検索

01 Come To Daddy (Pappy Mix).mp3 !

!

!

イテレータでファイル検索

01 Come To Daddy (Pappy Mix).mp3 01 4.m4a !

!

イテレータでファイル検索

01 Come To Daddy (Pappy Mix).mp3 01 4.m4a 01 minipops 67 [120.2] [source field mix].m4a !

イテレータでファイル検索

01 Come To Daddy (Pappy Mix).mp3 01 4.m4a 01 minipops 67 [120.2] [source field mix].m4a 01 Windowlicker (Original Demo).mp3

イテレータでファイル検索

01 Come To Daddy (Pappy Mix).mp3 01 4.m4a 01 minipops 67 [120.2] [source field mix].m4a 01 Windowlicker (Original Demo).mp3 01 Windowlicker.mp3

イテレータでファイル検索

イテレータは

組み合わせられる

イテレータが

別のイテレータを

生成する

標準インターフェイス

としての

イテレータ

Generator

Generator• PHP5.5~

• Iterator の一種

•ひとつの関数だけで作れる

$gen = function () { yield 1; yield 2; yield 3; }; !

foreach ($gen() as $n) { echo $n, PHP_EOL; }

ジェネレータを使ってみる

1 !

ジェネレータを使ってみる

1 2

ジェネレータを使ってみる

1 2 3

ジェネレータを使ってみる

$range = function ($start, $end, $step = 1) { for ($i = 1; $i <= $end; $i += $step) { yield $i; } }; !

foreach ($range(1, 100) as $n) { echo $n, PHP_EOL; }

ジェネレータでrange

http://qiita.com/yuya_takeyama/items/51fb058ed20d3df8209e

$range = function ($start, $end, $step = 1) { for ($i = 1; $i <= $end; $i += $step) { yield $i; } }; !

foreach ($range(1, 100) as $n) { echo $n, PHP_EOL; }

ジェネレータでrange

$range = function ($start, $end, $step = 1) { for ($i = 1; $i <= $end; $i += $step) { yield $i; } }; !

foreach ($range(1, 100) as $n) { echo $n, PHP_EOL; }

ジェネレータでrange

圧倒的に 簡潔

Functional Programming

with Generator

nikic/iter

•コレクション操作関数群

• range/map/filter/reduce •イテレータを受けてジェネレータを返す

ひとつのことをうまくやれ

全てのプログラムをフィルタに

部分の総和は全体よりも大きい

まとめ•Stream と Iterator は PHP の標準インターフェイスである

•これらをフィルタする関数を組み合わせることで、あらゆるロジックを表現することができる

•ジェネレータにより柔軟にイテレータを生成できる

Thank you for

Listening