[php][Interface] PHPのイテレータインターフェース実装
Zend Frameworkをみているとこのこの辺など結構使っているようなのでちょっと勉強してみました。
今回はイテレータについてです。イテレータとはwikipediaによれば、「プログラミング言語において配列やそれに類似するデータ構造の各要素に対する繰返し処理の抽象化である」とあります。
自分もあまり詳しくないので完全に理解できてはいないのですが、おそらく一つのクラスだけでイテレータを実装してもあまりいいことはなく、いろいろなクラスでイテレータを実装することで繰り返しの処理を統一化できて便利になるのだと思います。たとえばforeach文は最も良い例ではないかと思います。定義済みのIteratorインターフェースを実装することで、このクラスのオブジェクトはイテレータとなりforeach文で処理を繰り返し処理を行うことができるようになります。
Iteratorにはcurrent(), key(), next(), rewind(), valid()のメソッドのインターフェースが定義されていますのでこれをサブクラスで実装します。各メソッドは以下のような処理を行います。
- current()
現在の要素の値を返す - key()
現在の要素のキーを返す - next()
次の要素へ内部ポインタを移動する - rewind()
内部ポインタを最初の要素へ戻す - valid()
next()やrewindのあとに呼ばれて、現在の要素が存在する場合にtrueを返す
試しに、偶数番目の要素のみを返す配列のイテレータを作ってみました。
< ?php class EvenArray implements Iterator { private $_array = null; private $_size = 0; private $_pos = 0; public function __construct($array) { if(!is_array($array)) { $array = array($array); } $this->_array = $array; $this->_size = count($array); $this->_pos = 0; } public function current() { if($this->_pos > $this->_size) { throw new Exception('Index is out of bounds'); } return $this->_array[$this->_pos]; } public function key() { if($this->_pos > $this->_size) { throw new Exception('Index is out of bounds'); } return (string)$this->_pos; } public function next() { return $this->_pos += 2; } public function rewind() { return $this->_pos = 0; } public function valid() { return isset($this->_array[$this->_pos]); } }
このクラスを生成して、foreachで以下のように繰り返しの処理を行うことができます。
$array = array(0,1,2,3,4,5,6,7,8,9); $earray = new EvenArray($array); foreach($earray as $key=>$value) { echo "key=$key, value=$value\n"; }
結果は以下のとおりです。
key=0, value=0 key=2, value=2 key=4, value=4 key=6, value=6 key=8, value=8
foreach($arg as $k=>$v)では以下のような順番で処理が行われているものと推測されます。
$iterator->rewind(); $iterator->valid(); (falseなら終了) $v = $iterator->current(); $k = $iterator->key(); ユーザーの定義した処理を実行。 $iterator->next(); ($iterator->valid(); へ戻る)