Iterator 是否仅适用于数值数组?

Does Iterator work only with numeric arrays?

我在 w3schools 看到了一个例子:

<?php
// Create an Iterator
class MyIterator implements Iterator {
  private $items = [];
  private $pointer = 0;

  public function __construct($items) {
    // array_values() makes sure that the keys are numbers
    $this->items = array_values($items);
  }

  public function current() {
    return $this->items[$this->pointer];
  }

  public function key() {
    return $this->pointer;
  }

  public function next() {
    $this->pointer++;
  }

  public function rewind() {
    $this->pointer = 0;
  }

  public function valid() {
    // count() indicates how many items are in the list
    return $this->pointer < count($this->items);
  }
}

// A function that uses iterables
function printIterable(iterable $myIterable) {
  foreach($myIterable as $item) {
    echo $item;
  }
}

// Use the iterator as an iterable
$iterator = new MyIterator(["a", "b", "c"]);
printIterable($iterator);
?>

如果它是关联数组而不是关联数组,当前方法是否可以循环数组numeric.If是的,我该怎么做?例如,我们可以做这样的事情吗:

function printIterable(iterable $myIterable) {
  foreach($myIterable as $item => $value) {
    echo  "$item - $value";
  }
}

// Use the iterator as an iterable
$iterator = new MyIterator(["a"=>1, "b"=>2, "c"=>3]);
printIterable($iterator);

当我尝试的时候。它打印这个:0 - 11 - 22 - 3

PHP 以相同的方式处理数字和关联数组,打印它们时没有区别,并且相同的代码适用于两者(好吧,除非有人开始添加花哨的功能..)

是的,可以打印关联数组。

问题是您的代码通过将 $pointer 属性 初始化为 0(即数值)来假设数据数组键是数字。可以通过使用 PHP 数组 ($this->items) 的内部指针,或者保留数组键的显式列表来解决这个问题。

版本 1 PHP 具有遍历数组的函数,它们被命名为 next()、current()、key() 等,并列在 https://www.php.net/manual/en/ref.array.php 下。这样就可以在没有显式指针的情况下遍历数组:

while(key($arr) !== null) {
  echo current($arr)."\n";
  next($arr);
}

如果在 https://www.php.net/manual/en/class.iterator.php 下指定的方法在您的 class(implements Iterator) 中实现,则可以使用这些函数,如下所示:

class MyIterator implements Iterator {
  private $items = [];
  private $pointer = 0;

  public function __construct($items) {
    $this->items = $items;
  }

  public function current() {
    return current($this->items);
  }

  public function key() {
    return key($this->items);
  }
  // ...

可以看出,它或多或少地输入了两次相同的调用。但是,如果您有一个更复杂的 Iterable 并且添加了其他代码片段,那么它可能开始变得值得工作了。

版本 2 如果您不想依赖版本 1 中的函数,您仍然可以使用 $this->pointer,但您必须保留数组键的显式列表:

class MyIterator implements Iterator {
  private $items = [];
  private $keys = [];
  private $pointer = 0;

  public function __construct($items) {
    $this->items = array_values($items);
    $this->keys = array_keys($items);
  }

  public function current() {
    return $this->items[$this->keys[$this->pointer]];

    // Alternative version:
    return $this->items[$this->key()];
  }

  // Return the key of the current element
  public function key() {
    return $this->keys[$this->pointer];
  }

  // Step the array one element, and then return that value
  public function next() {
    // Check that we're not moving out of bounds, if so return null
    // and follow the next() spec.

    ++$this->pointer;
    return $this->items[$this->keys[$this->pointer]];
    
    // Alternative version:
    return $this->current();
  }
  // ...

正如所见,版本 2 创建了另一个数组“级别”来跟踪(如果关联数组和数字数组都应该被支持)。但是可以做到。