メールの詳細(トピック表示)
Iterator patterns in PHP4
投稿者:TAKAHASHI Masayoshiさん 2001/09/18 12:15 MLNo.1005 [メール表示]
高橋正義です。
イテレータのサンプルですが、PHP4版も作ってみました。
PHPは、arrayが順序つきHash(?)である、という何かがすごい言語で、
array自体はクラスではないため、継承ではなく委譲する使い方に
なります。
また、arrayにはnextという、要素を一つずつ取り出す関数がある
のですが、これは「arrayの要素がもうない時」と「要素がFALSE
だった時」の区別をしないらしいので、eachという関数が推奨
されているようです(「PHPマニュアル」より)。
each関数は、左辺が list($foo, $bar) となっていると、
$fooにキーが、$barに値が入ります(いや、だからarrayはハッシュ
なんです……)。そして、arrayの最後まで来たときにはeach関数は
FALSEを返します。
<?php
// Iterator Smaple
class Book {
var $name;
function Book($name) {$this->name = $name;}
}
class BookShelf {
var $books;
function BookShelf() {$this->books = array();}
function book_at($index) {return $this->books[$index];}
function append_book($book) {array_push($this->books, $book);}
function length() {return size($this->books);}
function iterator() {return new BookShelfIterator($this);}
}
class BookShelfIterator {
var $bookShelf;
function BookShelfIterator($bookShelf) {
$this->bookShelf = $bookShelf;
reset($this->bookShelf->books);
}
function next() {
return each($this->bookShelf->books);
}
}
// main
$bookShelf = new BookShelf();
$bookShelf->append_book(new Book("Around the World in 80 days"));
$bookShelf->append_book(new Book("Bible"));
$bookShelf->append_book(new Book("Cinderella"));
$bookShelf->append_book(new Book("Daddy-Long-Legs"));
$it = $bookShelf->iterator();
while (list($pos, $book) = $it->next()) {
print $book->name."<br>\n";
}
?>
また、PHP4にはarray_walkという、配列の要素一つずつに関数を適用して
くれる関数があります。これと匿名関数を使えば、以下のようにも
書けます。
BookShelfIterator の iterate というメソッドがイテレータを実行
するためのメソッドで、これの引数は匿名関数、またはユーザ関数の
関数名です。create_functionの引数は、一つ目が匿名関数の引数、
二つ目が匿名関数の定義(を文字列で表したもの)です。
このようにすれば、PHP4でインターナルイテレータを実装できます。
<?php
//Iterator Sample 2
// BookクラスとBookShelfクラスは上と同じなので省略
class BookShelfIterator {
var $bookShelf;
var $func;
function BookShelfIterator($bookShelf) {
$this->bookShelf = $bookShelf;
}
function iterate($func){
reset($this->bookShelf->books);
array_walk($this->bookShelf->books, $func);
}
}
// main
$bookShelf = new BookShelf();
$bookShelf->append_book(new Book("Around the World in 80 days"));
$bookShelf->append_book(new Book("Bible"));
$bookShelf->append_book(new Book("Cinderella"));
$bookShelf->append_book(new Book("Daddy-Long-Legs"));
$it = $bookShelf->iterator();
$it->iterate(create_function('$book','print $book->name."<br>\n";'));
?>
高橋征義 (TAKAHASHI Masayoshi) Email:maki@…
読み込み中...-
MLNo.1006
Hideto ISHIBASHIさん
(0) 2001/09/18 16:01 [メール表示する]

石橋秀仁といいます。新参ものです。
高橋さん、こちらのMLでもご活躍ですね。
Rubyのサンプルコードを参考にさせていただいています。
> PHPは、arrayが順序つきHash(?)である、という何かがすごい言語で、
そうですね、CとPerlとオブジェクト指向の絶妙なバランスが、
えもいわれぬ不快、おっと、深い味わいを醸し出しています。
> また、arrayにはnextという、要素を一つずつ取り出す関数がある
> のですが、これは「arrayの要素がもうない時」と「要素がFALSE
> だった時」の区別をしないらしいので、eachという関数が推奨
> されているようです(「PHPマニュアル」より)。
ふつうの言語ならイテレータの指すところが「最後の要素の次」
かを調べる関数(lastとでも名づけましょうか)があるのですが、
無いので困っています。C++で外部イテレータを覚えた人は、
PHPの外部イテレータには戸惑うことだと思います。
# また、現在の場所を調べるcurrentという関数も、「最後」と「FALSE」
# の区別をしないので、ほとんど役に立ちません。
あと、私ならこういう風にするというコードを載せておきます。
いえ、手元にPHP3しかないので、それで動くようにしただけです。
このコードでの美しさは高橋さんのコードのほうが上です。
function構文で、関数を関数として書けるという点では、
匿名関数よりも読みやすくなります。行数が増えればメリットが
あります。匿名関数の意味を考えれば本質的には同じことですが。
匿名関数を使うのは、このコードの応用編と言えると思います。
高橋さんの紹介された匿名関数によるコードは内部イテレータ
と呼べると思います。これも同じ手法です。
ただしPHP3には匿名関数が無いので、関数ポインタのような
コールバック方法を用いています。
# 手元のPHP3にはarray_pushがないので[]=で代用しました。
# array_walkはPHP3にもあるようですね。
*** iter2.orig.php Tue Sep 18 15:32:09 2001
--- iter2.php Tue Sep 18 15:34:35 2001
***************
*** 8,14 ****
var $books;
function BookShelf() {$this->books = array();}
function book_at($index) {return $this->books[$index];}
! function append_book($book) {array_push($this->books, $book);}
function length() {return size($this->books);}
function iterator() {return new BookShelfIterator($this);}
}
--- 8,14 ----
var $books;
function BookShelf() {$this->books = array();}
function book_at($index) {return $this->books[$index];}
! function append_book($book) {$this->books[] = $book;}
function length() {return size($this->books);}
function iterator() {return new BookShelfIterator($this);}
}
***************
*** 21,27 ****
}
function iterate($func){
reset($this->bookShelf->books);
! array_walk($this->bookShelf->books, $func);
}
}
--- 21,29 ----
}
function iterate($func){
reset($this->bookShelf->books);
! while (list($pos, $book) = each($this->bookShelf->books)) {
! $func($pos, $book);
! }
}
}
***************
*** 32,37 ****
$bookShelf->append_book(new Book("Cinderella"));
$bookShelf->append_book(new Book("Daddy-Long-Legs"));
$it = $bookShelf->iterator();
! $it->iterate(create_function('$book','print $book->name."
\n";'));
?>
--- 34,40 ----
$bookShelf->append_book(new Book("Cinderella"));
$bookShelf->append_book(new Book("Daddy-Long-Legs"));
$it = $bookShelf->iterator();
! function func($pos, $book) { print $book->name."
\n"; }
! $it->iterate('func');
?>
# このコードの差を見てPHPは進化していると言えるでしょうか?
# C++のように肥大化の道をたどらなければよいのですが・・・
--
石橋秀仁 hideto-i@…


