イテレータを介して見るPHPクラスの内部構造 を読んだのですが、 最近の php はちゃんと Python などの言語を参考にしてるなーと思うことが多かったのにこの仕様は残念です。
Python など多くの言語では、イテレータの取得とイテレータの使用を明確に区別しています。 Python ではイテレータを取得できるオブジェクトを iterable と呼び、イテレータを iterator と呼びます。
iterable は __iter__
メソッドを実装し、 iterator は __next__
メソッド (Python 2 では next
)を実装します。
また、 iterator は同時に iterable も実装し、自分自身を返すようにします。
これにより、 for 文などは「イテレータを取得しそれをイテレートする」というシンプルな動作ができます。
iterable と iterator を愚直に実装すると次のようになります。
class Foo(object): def __init__(self, L): self._L = L def __iter__(self): return FooIterator(self) class FooIterator(object): def __init__(self, foo): self._i = 0 self._foo = foo def __iter__(self): return self def __next__(self): try: v = self._foo._L[self._i] self._i += 1 return v except IndexError: raise StopIteration next = __next__ # For Python 2 compatibility.
使ってみましょう。
>>> foo = Foo([1,2,3]) >>> ifoo = iter(foo) # イテレータの取得 >>> next(ifoo) # その巡回 1 >>> for v in foo: # イテレータの取得と巡回 ... print(v) ... 1 2 3 >>> for v in ifoo: # イテレータからイテレータを取得するとそれ自体が返るので、「続き」を for ループできる ... print(v) ... 2 3
iterator と iterable の違いが判っていれば、とても素直な実装に見えるでしょう。
さて、 Python のジェネレータは同時に iterator です。なので、先ほどのコードはこう書けます。
class Foo(object): def __init__(self, L): self._L = L def __iter__(self): for v in self._L: yield v
もちろん、 __iter__
は iterator を返せばいいのですから、次のように書いてもOKです。
class Foo(object): def __init__(self, L): self._L = L def __iter__(self): return iter(self._L)
php も __getIterator()
メソッドとか用意して、ジェネレータなどを使って簡単にイテレータを返せるようになると便利になると思います。
(追記) ちゃんと php にも IteratorAggregate がありました。 問題はAPI設計ではなく、 SplQueue などが使ってる C 言語レベルのインタフェースと IteratorAggregate インタフェースがリンクしていないという オブジェクトシステムの欠陥のようです。
php でイテレータを実装するときは、直接実装するのではなくまず IteratorAggregate の使用を検討しましょう。