1

その名前から、PHP のNoRewindIterator巻き戻しがいつ行われるかは完全に明らかです。

しかし、時々、特に。それから拡張するとき、私は自問します:巻き戻しはありますか?1回巻き戻しますか?必要に応じて、(最初の) 巻き戻しを実装するTraversable必要がありますか?

ここで内部イテレータについて特に疑問に思うので、IteratorクラスNoRewindIteratorはそのコンストラクタでパラメータとして受け取ります。

例えば:

$iterator = new SomeIterator(); // implements Iterator
$noRewind = new NoRewindIterator($iterator);
foreach ($noRewind as $value) {
    break;
}

これから$iterator->rewind()使用する場合は呼び出さ$noRewindれませんか?または、最初に呼び出されたときに一度$noRewind->rewind()呼び出されます (たとえばforeach、例の最初の内)。それとも建設中ですか?

4

2 に答える 2

1

クラスの名前のみ ( NoRewindIterator)のように、マニュアルには具体的に次の文言があります。

NoRewindIterator - この反復子は巻き戻しできません。

具体的な方法については、次のとおりです。

NoRewindIterator::rewind() - 内側の反復子での巻き戻し操作を防ぎます。

これは、Iterator::rewind()メソッドが内部反復子に渡されないことを意味します。テストもこれを示しています。これは私が実行した単純なものです(PHPの一部ではないすべてのイテレータのコードはIterator Gardenにあります):

$iterator = new RangeIterator(1, 1);
$debug    = new DebugIteratorDecorator($iterator);
$noRewind = new NoRewindIterator($debug);

echo "first foreach:\n";

foreach ($noRewind as $value) {
    echo "iteration value: $value\n";
}

このコードでは、debug-iterator が反復情報をオンザフライで出力 (エコー) します。

first foreach:
Iterating (RangeIterator): #0 valid()
Iterating (RangeIterator): #0 parent::valid() is TRUE
Iterating (RangeIterator): #0 current()
Iterating (RangeIterator): #0 parent::current() is 1
iteration value: 1
Iterating (RangeIterator): #1 next()
Iterating (RangeIterator): #1 after parent::next()
Iterating (RangeIterator): #1 valid()
Iterating (RangeIterator): #1 parent::valid() is FALSE

これが示すように、$iterator->rewind()は呼び出されません。

これは、関連する質問で与えられたのと同じ理由からも理にかなっています: Why must I rewind IteratorIterator . は親クラスNoRewindIteratorから拡張され、親クラスIteratorIteratorとは異なりgetInnerIterator()、メソッドは ではIteratorなく を返しますTraversable

この変更により、次の必要がある場合に巻き戻しを初期化できます。

echo "\n\$calling noRewind->getInnerIterator()->rewind():\n";

$noRewind->getInnerIterator()->rewind();

echo "\nsecond foreach:\n";

foreach ($noRewind as $value) {
    echo "iteration value: $value\n";
}

デバッグ出力の例:

$calling noRewind->getInnerIterator()->rewind():
Iterating (RangeIterator): #0 rewind()
Iterating (RangeIterator): #0 after parent::rewind()

second foreach:
Iterating (RangeIterator): #0 valid()
Iterating (RangeIterator): #0 parent::valid() is TRUE
Iterating (RangeIterator): #0 current()
Iterating (RangeIterator): #0 parent::current() is 1
iteration value: 1
Iterating (RangeIterator): #1 next()
Iterating (RangeIterator): #1 after parent::next()
Iterating (RangeIterator): #1 valid()
Iterating (RangeIterator): #1 parent::valid() is FALSE

これらの詳細を知っていればOneTimeRewindIterator、たとえば次のようなものを作成できます。

/**
 * Class OneTimeRewindIterator
 */
class OneTimeRewindIterator extends NoRewindIterator
{
    private $didRewind = FALSE;

    public function rewind() {
        if ($this->didRewind) return;

        $this->didRewind = TRUE;
        $this->getInnerIterator()->rewind();
    }
}
于 2013-05-22T01:44:56.393 に答える