【问题标题】:RecursiveIteratorIterator returns extra elementsRecursiveIteratorIterator 返回额外的元素
【发布时间】:2012-11-13 09:36:56
【问题描述】:

如果rewind()while循环之前没有被调用,RecursiveIteratorIterator会返回额外的结果

例子

$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
  • 数组从未被初始化或调用,为什么 while 循环需要 $iterator-&gt;rewind()
  • foreach 完美运行,无需调用 rewind 使用迭代器时 foreachwhile 之间的区别是什么

Code In action

【问题讨论】:

  • 如果你不调用 rewind,IteratorIterator 的行为也会很奇怪。 stackoverflow.com/questions/2458955/…
  • @rambocoder 刚刚也观察到了......这看起来更像是一个错误
  • 我不理会它,因为如果我在写 spl,我的第一个直觉是编码它like this(只是转发方法调用),可能有一个 bool 变量来缓存返回值.但是,当我开始考虑像 CachingIterator 这样的东西时,整个迭代器/装饰器范式是否仍然可以与像 RecursiveIteratorIterator 这样的复杂迭代器一起工作,并且会在疯狂的时刻抛出异常/错误,如果没有,想起来太复杂了很多努力。

标签: php iterator


【解决方案1】:

我将按相反的顺序回答问题:

foreach 可以完美运行,而无需调用 rewind 在使用迭代器时 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

输出看起来有点奇怪,特别是第二次迭代错过了next() 调用,所以它只是停留在同一个元素上。

原因是 RecursiveIteratorIterator 实现中的一个特殊性:迭代器从 RS_START 状态开始,在此状态下的第一个 next 调用仅检查 hasChildren(),但实际上并不调用底层迭代器的next() 方法。完成此操作后,它会切换到RS_NEXT 模式,其中next() 调用正确发生。这就是为什么前进延迟了一步。

在我看来这是一个错误,但https://bugs.php.net/bug.php?id=44063 声称并非如此。

【讨论】:

  • 它看起来像一个 PHP 错误。如果您有非常重要的理由知道它并且这里没有人能够回答,请在 ext/spl/spl_iterators.c 中试试运气
  • 其他一些迭代器调用valid() 也会产生意想不到的结果,除非迭代器没有正确初始化,即在调用__contruct() 以及在valid() 之前调用rewind()可以调用。如果你不能解决这个问题,你可以用CachingIterator 换行。
猜你喜欢
  • 1970-01-01
  • 2013-08-01
  • 1970-01-01
  • 2020-07-10
  • 1970-01-01
  • 2023-03-09
  • 2014-05-05
  • 2021-11-29
  • 1970-01-01
相关资源
最近更新 更多