NoRewindIterator何时回退内部迭代器?

时间:2013-05-22 01:44:56

标签: php iterator spl

通过它的名字,当PHP的NoRewindIterator倒带时,非常清楚:永远不会。

但有时候,尤其是从它延伸时,我问自己:它是否会倒带?它会倒回一次吗?因此,如果我需要它,我是否需要实现(第一次)倒带,因为某些 Traversable 可能需要它?

我特别想知道这里的内部迭代器,所以Iterator NoRewindIterator类作为参数与其构造函数一起使用。

例如:

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

从这里开始使用$iterator->rewind()时,永远不会调用$noRewind吗?或者,第一次调用$noRewind->rewind()时会调用一次(例如,在示例中的第一个foreach内)。或者也许在构造?

2 个答案:

答案 0 :(得分:1)

仅仅通过班级名称(NoRewindIterator),the manual has具体说明如下:

  

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";
}

在此代码中,调试迭代器即时打印(回显)迭代信息:

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 IteratorIteratorNoRewindIteratorIteratorIterator不同延伸到其父类,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();
    }
}

答案 1 :(得分:0)

在某些情况下,

NoRewindIterator可以倒带。

E.g。当你继续在外面循环时使用:

<?php
$iterator = new NoRewindIterator(new ArrayIterator([1, 2, 3, 4]));
foreach ([1, 2, 3, 4] as $a) {
    foreach ($iterator as $b) {
        echo "$a\t$b\n";
        continue 2;
    }
}

可以预期它会打印出一个,两个两个,依此类推,但实际上这个代码打印出来了:

1   1
2   1
3   1
4   1

使用普通continue 2交换break不会改变任何事情。

<?php
$iterator = new \NoRewindIterator(new \ArrayIterator([1, 2, 3, 4]));

$array = [1, 2, 3, 4];

foreach ($array as $a) {
    foreach ($iterator as $b) {
        echo "$a\t$b\n";
        break;
    }
}

如果用调试迭代器包装这个迭代器,你会发现如果你以任何方式中断循环,next()永远不会被调用。因此,您总是会收到第一个值。但似乎迭代器以某种方式得到了回归。