【问题标题】:When does the NoRewindIterator rewind the inner Iterator?NoRewindIterator 什么时候回退内部迭代器?
【发布时间】:2013-05-16 22:33:38
【问题描述】:

从它的名字看,当 PHP 的 NoRewindIterator 回退时非常清楚:从不。

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

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

例如:

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

从现在开始使用$noRewind 时将永远不会调用$iterator->rewind() 吗?或者它会在第一次调用$noRewind->rewind() 时被调用一次(例如,在示例中的第一个foreach 内)。或者可能正在构建中?

【问题讨论】:

    标签: php iterator spl


    【解决方案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 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();
        }
    }
    

    【讨论】:

    【解决方案2】:

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

    例如当你使用 continue 到外面循环时:

    <?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
    

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

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

    【讨论】:

      猜你喜欢
      • 2016-01-11
      • 1970-01-01
      • 1970-01-01
      • 2018-11-07
      • 1970-01-01
      • 1970-01-01
      • 2012-07-06
      • 2021-12-03
      • 2016-09-23
      相关资源
      最近更新 更多