PHP的Iterator方法如何有效(),current()和next()表现如何?

mev*_*eva 9 php

我正在使用MAMP的 PHP 5.3.6 .

我有一个使用情况下这将是最好使用PHP的Iterator接口中的方法,next(),current()valid()迭代通过集合.一个foreach循环将不是在我的特殊情况为我工作.简化的while循环可能看起来像

<?php
while ($iter->valid()) {
  // do something with $iter->current()
  $iter->next();
}
Run Code Online (Sandbox Code Playgroud)

当$ iter实现PHP的Iterator接口时,上面的代码是否总能工作?PHP的foreach关键字如何处理迭代器?

我问的原因是我写的代码可能会给出一个ArrayIterator或一个MongoCursor.两者都实现了PHP的Iterator接口,但它们的行为方式不同.我想知道PHP或PHP的Mongo扩展中是否存在错误.

ArrayIterator::valid()任何调用之前返回true next()- 在ArrayIterator创建之后立即返回true .

MongoCursor::valid()第一次调用返回true next().因此,上面的while循环永远不会执行.

有可能是冗长的,下面的代码演示了这些断言:

<?php

// Set up array iterator
$arr = array("first");
$iter = new \ArrayIterator($arr);

// Test array iterator
echo(($iter->valid() ? "true" : "false")."\n"); // Echoes true
var_dump($iter->current()."\n");                // "first"
$iter->next();
echo(($iter->valid() ? "true" : "false")."\n"); // Echoes false


// Set up mongo iterator
$m = new \Mongo();
$collection = $m->selectDB("iterTest")->selectCollection("mystuff");
$collection->drop(); // Ensure collection is empty
$collection->insert(array('a' => 'b'));
$miter = $collection->find(); // should find one object

// Test mongo iterator
echo(($miter->valid() ? "true" : "false")."\n"); // Echoes false

$miter->next();

echo(($miter->valid() ? "true" : "false")."\n"); // Echoes true
var_dump($miter->current());                     // Array(...)
Run Code Online (Sandbox Code Playgroud)

哪种实施是正确的?我找到了很少的文档来支持这两种行为,官方的PHP文档要么模糊不清,要么我读错了.国家文件 Iterator::valid():

在Iterator :: rewind()和Iterator :: next()之后调用此方法以检查当前位置是否有效.

这表明我的while循环应首先调用next().

然而, Iterator::next状态的PHP文档:

在每个foreach循环之后调用此方法.

这表明我的while循环是正确的.

总结一下 - PHP迭代器应该如何表现?

Jaz*_*azz 9

这是个有趣的问题.我不确定为什么一个foreach不适合你,但我有一些想法.

看一下Iterator接口参考页面上给出的示例.它显示了PHP 内部实现foreach调用Iterator方法的顺序.特别要注意的foreach是,首次设置时,第一次调用是rewind().这个例子虽然没有很好的注释,但却是我答案的基础.

我不确定为什么调用之前MongoCursor不会返回true ,但是你应该能够通过在循环之前调用来重置任何类型的对象.所以你会:valid() next()rewind()

// $iter may be either MongoCursor or ArrayIterator

$iter->rewind();
while( $iter->valid() ){
    // do something with $iter->current()
    $iter->next();
}
Run Code Online (Sandbox Code Playgroud)

我相信这对你有用.如果没有,Mongo类可能会有错误.

编辑:Mike Purcell的回答正确地指出ArrayIterator和Iterator是不一样的.但是,ArrayIterator实现了Iterator,因此您应该可以使用rewind()我在其中任何一个上面显示的内容.


Gor*_*don 8

对任何迭代器进行子类化并在调用它时回显将告诉您它的行为方式.

示例(演示)

class MyArrayIterator extends ArrayIterator
{
    public function __construct ($array)
    {
        echo __METHOD__, PHP_EOL;
        parent::__construct($array);
    }
    …
}

foreach (new MyArrayIterator(range(1,3)) as $k => $v) {
    echo "$k => $v", PHP_EOL;
}
Run Code Online (Sandbox Code Playgroud)

产量

MyArrayIterator::__construct
MyArrayIterator::rewind
MyArrayIterator::valid
MyArrayIterator::current
MyArrayIterator::key
0 => 1
MyArrayIterator::next
MyArrayIterator::valid
MyArrayIterator::current
MyArrayIterator::key
1 => 2
MyArrayIterator::next
MyArrayIterator::valid
MyArrayIterator::current
MyArrayIterator::key
2 => 3
MyArrayIterator::next
MyArrayIterator::valid
Run Code Online (Sandbox Code Playgroud)

这相当于做

$iterator = new MyArrayIterator(range(1,3));
for ($iterator->rewind(); $iterator->valid(); $iterator->next()) {
    echo "{$iterator->key()} => {$iterator->current()}", PHP_EOL;
}
Run Code Online (Sandbox Code Playgroud)

调用方法的顺序与自定义相同Iterator:

class MyIterator implements Iterator
{
    protected $iterations = 0;
    public function current()
    {
        echo __METHOD__, PHP_EOL;
        return $this->iterations;
    }
    public function key ()
    {
        echo __METHOD__, PHP_EOL;
        return $this->iterations;
    }
    public function next ()
    {
        echo __METHOD__, PHP_EOL;
        return $this->iterations++;
    }
    public function rewind ()
    {
        echo __METHOD__, PHP_EOL;
        return $this->iterations = 0;
    }
    public function valid ()
    {
        echo __METHOD__, PHP_EOL;
        return $this->iterations < 3;
    }
}
foreach (new MyIterator as $k => $v) {
    echo "$k => $v", PHP_EOL;
}
Run Code Online (Sandbox Code Playgroud)