1

顾名思义,当 PHPNoRewindIterator倒带时非常清楚:从不。

但有时,尤其是。当从它延伸时,我问自己:它会倒带吗?它会倒带一次吗?因此,如果我需要它,我是否需要像某些人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";
}

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

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()从不调用。

出于相关问题中给出的相同原因,这也是有道理的:为什么我必须倒带 IteratorIteratorNoRewindIterator扩展自它的父类IteratorIterator不同于它的父类,该getInnerIterator()方法返回 anIterator而不是 a 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 回答
0

NoRewindIterator在某些情况下可以倒带。

例如,当您使用 continue to a cycle 外:

<?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()如果你以任何方式中断循环,它就永远不会被调用。因此,您将始终收到第一个值。然而,迭代器似乎以某种方式倒带。

于 2018-03-14T10:20:28.727 回答