8

RecursiveIteratorIteratorループrewind()の前に呼び出されない場合、追加の結果を返しますwhile

$array = array("A","B","C");
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
//$iterator->rewind() ; this would fix it 
while ( $iterator->valid() ) {
    print($iterator->current()) ;
    $iterator->next();
}

出力

AABC  <--- Instead of ABC
  • なぜ余分Aではない のCですか?
  • 配列が開始または呼び出されたことがない理由$iterator->rewind()whileループが必要な理由
  • foreachイテレータを使用する場合とrewindの違いを呼び出さなくても完全に機能しますforeachwhile

動作中のコード

4

1 に答える 1

5

質問には逆の順序で答えます。

foreachrewindそれらの違いforeachwhileイテレータを使用する場合の違いを呼び出す必要なしに完全に機能します

foreach内部でへの呼び出しをrewind()行うので、自分で呼び出す必要はありません。これは常に行われるため、すでにイテレータを使用している場合でも、foreachループは最初からやり直します。(これは、でラップすることで回避できますNoRewindIterator)。

配列が開始または呼び出されたことはありません。whileループに$iterator->rewind()が必要な理由

SPLイテレータはforeach、この場合、メソッド呼び出しの重複を回避するために使用されるように設計されています。RecursiveIteratorIteratorが構築時にメソッドを呼び出す場合、ループの開始RecursiveArrayIterator::rewind()時に再度呼び出されます。foreachそのため、呼び出しは行われません。

なぜ余分なAがCではないのですか?

これを理解するには、RecursiveArrayIterator実際に呼び出されるメソッドを確認するのが便利です。

<?php

class DebugRAI extends RecursiveArrayIterator {
    public function rewind() { echo __METHOD__, "\n"; return parent::rewind(); }
    public function current() { echo __METHOD__, "\n"; return parent::current(); }
    public function key() { echo __METHOD__, "\n"; return parent::key(); }
    public function valid() { echo __METHOD__, "\n"; return parent::valid(); }
    public function next() { echo __METHOD__, "\n"; return parent::next(); }
}

$array = array("A", "B", "C");
$iterator = new RecursiveIteratorIterator(new DebugRAI($array));
while ($iterator->valid()) {
    echo $iterator->current(), "\n";
    $iterator->next();
}

これにより、次の出力が生成されます。

DebugRAI::valid
DebugRAI::current
A
DebugRAI::valid
DebugRAI::valid
A
DebugRAI::next
DebugRAI::valid
DebugRAI::valid
DebugRAI::current
B
DebugRAI::next
DebugRAI::valid
DebugRAI::valid
DebugRAI::current
C
DebugRAI::next
DebugRAI::valid
DebugRAI::valid

出力は少し奇妙に見えます。特に、2回目の反復ではnext()呼び出しが欠落しているため、同じ要素にとどまります。

この理由は、RecursiveIteratorIterator実装の特殊性です。イテレータは状態で開始し、このRS_START状態での最初のnext呼び出しはチェックするだけhasChildren()で、実際には基になるイテレータのnext()メソッドを呼び出しません。これが行われた後、それは呼び出しが適切に行われるRS_NEXTモードに切り替わります。next()そのため、前進が1ステップ遅れます。

私の目にはこれはバグですが、https://bugs.php.net/bug.php?id=44063はそうではないと主張しています。

于 2012-11-25T22:00:46.147 に答える