
こんにちは、Habr。
たまたま最近、
Pro PHPという素晴らしい本を手に入れました。この本では、セクション全体がイテレーターに当てられています。 はい、このトピックはすでにHabréで(そしておそらく複数回)提起されていることは知っていますが、それでもこの記事を追加できるようにするつもりです。 上記の記事の例のほとんどは、現実とはまったく異なります。 そして-もしあなたがイテレータの助けを借りて解決しようとしている本当の問題に興味があるなら-catへようこそ。
イテレーターはどのような動物ですか?
実際、イテレーターは、子要素の特定の走査を単純化できるオブジェクトです。 phpには
Iteratorインターフェースがあり、これを実装することで目的の効果を実現できます。 SPL(
Standart PHP Library )には、最も一般的で人気のあるイテレーターを実装するいくつかのクラスも含まれています。 彼らのリストは
ここにあります 。
では、なぜイテレータが必要なのか-配列だけを使用できますか?
PHPでは、オブジェクトまたはデータの列挙が配列に単純に「追加」され、その要素をソートできることが歴史的に何らかの形で発生しました。 9つの等しい部分に分割された正方形(マップなど)で表される要素の特定のフィールドからのデータがある状況を想像してください。 そして、あなたはすべての正方形を時計回りに回る必要があり、配列ではそれらはランダムに折り畳まれます。 あまり便利ではありませんか?
したがって、この場合、イテレータが役立ちます。 配列に要素を追加する代わりに、それらをイテレータに追加すると、それらを簡単に反復処理できます。 隣接するマップ要素の列挙を実装するコードの例は、次のとおりです。
class TileIterator implements Iterator { private $_side = 'north_west'; private $_neighbours = array(); private $_isValid = true; public function __construct($neighboursArray) { $this->_side = 'north_west'; $this->_neighbours = $neighboursArray; } function rewind() { $this->_side = 'north_west'; } function current() { return $this->_neighbours[$this->_side]; } function key() { return $this->_side; } function next() { switch ($this->_side) { case 'north_west': $this->_side = 'north'; break; case 'north': $this->_side = 'north_east'; break; case 'north_east': $this->_side = 'east'; break; case 'east': $this->_side = 'south_east'; break; case 'south_east': $this->_side = 'south'; break; case 'south': $this->_side = 'south_west'; break; case 'south_west': $this->_side = 'west'; break;
そして今、実際の呼び出し:
それでは、誰にでも馴染みのある正しい順序でのみつぶします:
foreach ($tiles as $tile) { ... }
はい、本当に悪くありません。 イテレータで他に何ができますか?
このトピックは非常に広範囲にわたるため、お気に入りの例を検討します。
LimitIteratorは、コードをデバッグまたはテストするときに非常に便利です。 特に、
PHPExcelで作業する
場合 、文字列の
反復処理で、ライブラリは
RowIteratorクラスを使用します。
このクラスの名前はIteratorであることを意味します。 ドキュメントを解析するたびにすべての行が「ドラッグ」されるのを避けるために、
LimitIteratorで
RowIteratorをラップし、数十行だけで作業できます。
FilterIteratorクラスを使用すると、オンザフライでデータを簡単にフィルタリングできます。 ある意味では、これはSQLクエリのWHERE部分に似ています。 たとえば、SDKがユーザーオブジェクトを返す
BaseCamp Classic APIなどのサードパーティAPIを使用しているとします。 また、プロジェクトの変更については、一部をemialで通知する必要があります。 そして、3つのパラメーターに従って除外する必要があります:電子メール、ID、および名前。 前述のクラスにより、これを簡単に実行してサポートできます。
class NotificationFilter extends FilterIterator { private $_skip; public function __construct(Iterator $iterator, $filter) { if (!is_array($filter)) { throw new InvalidArgumentException("Filter should be an array. ".gettype($filter)." given."); } parent::__construct($iterator); $this->_skip = $filter; } public function accept() {
したがって、NotificationFilter :: accept()メソッドでは、1人のユーザーのみを操作します。
また、
RecursiveIteratorIteratorを使用して
多次元配列を1次元配列に簡単に
キャストでき
ます。RecursiveDirectoryIteratorなどを使用して
ディレクトリのファイルリストを取得すると便利です。
そして、宇宙プログラムはどこにありますか?
はい、ほとんど忘れていました。 イテレーターで「遊んで」いる間、イテレーターの使い方を自分で理解しようとして、次のアイデアがありました
-GameDevハブと
Web開発の両方にある
Habrの投稿だけを読むにはどうすればいいですか? フィードでは、両方のハブから投稿を読み取ることができますが、意味がわかっていれば、投稿の共通部分は閲覧できません。 その結果、イテレーターを使用した小さなプロジェクトができました。
すべてのプロジェクトコード
はBitBucketリポジトリにありますが、ここでは最も興味深い部分のみを公開します。 以下のコード:
class HabraPost { public $name = ''; public $url = ''; public $hubs = null; public static $baseUrl = 'http://habrahabr.ru/hub/'; protected static $fullHubList = array( 'infosecurity' => ' ', 'webdev' => '-', 'gdev' => 'Game Development', 'DIY' => 'DIY ', 'pm' => ' ', 'programming' => '', 'space' => '', 'hardware' => '', 'algorithms' => '', 'image_processing' => ' ', ); public function __construct($name, $url, $hubs = array()) { $this->name = $name; $this->url = $url; $this->hubs = $hubs; } public static function getFullHubsList() { $list = self::$fullHubList; asort($list); return $list; } } class PostsStorage { private $_iterator; public function __construct() { $this->_iterator = new SplObjectStorage(); } public function save(HabraPost $post) {
だから-アイデアは非常に簡単です:
- ユーザーは1つ以上のハブを選択し、
- 利用可能なHabrページを反復処理し、コンテンツへのリンクを収集し、
- これらすべてを
PostsStorage
、 HabraPostFilter
使用したフィルタリング
その結果、スクリーンショットに似たものが得られます。
Habra効果に耐えられるホスティングを誰かが親切に提供してくれたら、プロジェクトを無料で利用できるようになります。
ご清聴ありがとうございました。
PS投稿へのコメントまたは個人的な通信で訂正/コメントを受け付けます。
UPD 誤字を訂正して
くれたNikita_Rogatnevに感謝します。
UPD デモをありがとう
hell0w0rd 。