@emonkak
Underscore.jsのようなコレクション処理ライブラリ(クローンではない)
Iteratorを利用した遅延リストが作れる(遅延評価とは言ってはいけない)
コレクションの各要素を並列処理できる
コレクション処理のメソッドをtraitでmix-inできる(RubyのEnumerableみたいな)
PHP 5.4〜5.5で動作 (0.20からtrait依存なので5.3は未サポート)
PHPのarray操作の関数がアレ(array_mapとか)
PHPのコレクション処理ライブラリは他にもあるけど遅延リストが作れなかった
PHP 5.5のGeneratorが遅延リストを作るのに最適
APIがUnderscore.js互換なら使いやすいよね
// 配列を返す実装のクラスをuseする
use Underbar\ArrayImpl as _;
// 要素を2倍にする関数
$double = function($x) {
return $x * 2;
};
// 要素を2倍にする
$xs = _::map([1, 2, 3, 4, 5], $double);
echo _::join($xs); // 2,4,6,8,10
// Iteratorを使った実装をuseする
// 5.5ならGeneratorImplも使える
use Underbar\IteratorImpl as _;
// 配列の要素を2倍にする($doubleの呼び出しは遅延される)
$xs = _::map([1, 2, 3, 4, 5], $double);
echo _::join($xs); // 2,4,6,8,10(ここで$doubleが呼ばれる)
// 偶数要素を選択($evenの呼び出しは遅延される)
$even = function($x) {
// 偶数ならtrue
return $x % 2 === 0;
};
$ys = _::select([1, 2, 3, 4, 5], $even);
echo _::join($ys); // 2,4(ここで初めて$evenが呼ばれる)
// $doubleは先頭の要素2つ分についてだけ呼び出される
echo _::join(_::take($xs, 2)); // 2,4
// 0から10万までの配列を走査
foreach (range(0, 100000) as $x) {
// 要素の数だけメモリを消費する
}
// 0から10万までの遅延リスト
// (組込みのrange()とは引数の仕様が違います)
foreach (_::range(0, 100001) as $x) {
// 要素1件分しかメモリを消費しない
}
// みんな大好きフィボナッチ数の無限リスト
$fibs = _::chain(array(1, 1))
->iterate(function($xs) {
return array($xs[1], $xs[0] + $xs[1]);
})
->map('Underbar\\IteratorImpl::first')
->take(20)
->toList(); // フィボナッチ数列を20個取り出す
// なんかすごく重い処理
$heayFunc = function($x) {
sleep(1);
return $x * 2;
};
// 処理結果を合計する
$result = _::chain(_::range(0, 10))
->parMap($heayFunc, 10)
->sum() // 0 + 2 + 4 + 6 + 8 + 10 + 12 + 14 + 16 + 18;
// 1秒強で終わります!
echo $result; // 90
並列処理はforkを使って実装されている
プロセス間通信はsocketで実装
シリアライズは組込みのserialize()とunserialize()
オーバヘッドが結構大きいので並列処理で本当に早くなるかベンチマークを
class Collection
{
// Enumerableをmixinする
use Underbar\Enumerable;
protected $array;
public function __construct()
{
// コンストラクターに指定された値をそのまま入れる
$this->array = func_get_args();
}
public function getUnderbarImpl()
{
// Iteratorを使った実装を使用する
return "Underbar\\IteratorImpl";
}
public function value()
{
// Underbarのメソッドの第一引数に渡す値
return $this->array;
}
}
$collection = new Collection(1, 2, 3);
// 2倍にする
$double = $collection
->map(function($n) { return $n * 2; })
->join($double));
// 要素を循環させる
$cycle = $collection
->cycle() // [1, 2, 3, 1, 2, 3, 1, ...]
->map(function($n) { return $n * 2; })
->take(6)
->join(); // 2,4,6,2,4,6