Возможно ли использование нескольких итераторов в php? - PullRequest
4 голосов
/ 14 марта 2010

ПОЖАЛУЙСТА, ПРОВЕРЬТЕ ОТВЕТЫ VolkerK, он предоставил другое решение, но я не могу пометить два сообщения как ответ. (


Добрый день!

Я знаю, что C # позволяет использовать несколько итераторов с помощью yield, как описано здесь: Возможно ли использование нескольких итераторов в c #?

В PHP есть и интерфейс Iterator. Можно ли реализовать более одного итерационного сценария для класса?

Подробнее (РЕДАКТИРОВАТЬ):

Например, у меня есть класс TreeNode, реализующий один узел дерева. Целое дерево может быть выражено с использованием только одного этого класса. Я хочу предоставить итераторы для итерации всех прямых и косвенных потомков текущего узла, например, с использованием порядка BreadthFirst или DepthFirst.

Я могу реализовать итераторы как отдельные классы, но для этого мне нужно, чтобы узел дерева представлял свою дочернюю коллекцию как общедоступную.

C # псевдокод:

 public class TreeNode<T> 
  {
  ...
     public IEnumerable<T> DepthFirstEnumerator
     {
         get
        {
            // Some tree traversal using 'yield return'
        }
     }

     public IEnumerable<T> BreadthFirstEnumerator
     {
         get
         {
             // Some tree traversal using 'yield return'
         }
     }
 }

Ответы [ 4 ]

5 голосов
/ 14 марта 2010

Да, вы можете.

foreach(new IteratorOne($obj) as $foo) ....

foreach(new IteratorTwo($obj) as $bar) .....

На самом деле, пока ваш класс реализует Iterator, вы можете применять к нему любой произвольный IteratorIterator. Это хорошо, потому что прикладные мета-итераторы не обязаны ничего знать о рассматриваемом классе.

Рассмотрим, например, такой итеративный класс, как этот

class JustList implements Iterator
{
    function __construct() { $this->items = func_get_args(); }
    function rewind()      { return reset($this->items); }
    function current()     { return current($this->items); }
    function key()         { return key($this->items); }
    function next()        { return next($this->items); }
    function valid()       { return key($this->items) !== null; }
}

Давайте определим некоторые мета-итераторы

class OddIterator extends FilterIterator {
    function accept() { return parent::current() % 2;  }
}

class EvenIterator extends FilterIterator {
    function accept() { return parent::current() % 2 == 0;  }
}

Теперь примените мета-итераторы к базовому классу:

 $list = new JustList(1, 2, 3, 4, 5, 6, 7, 8, 9);

 foreach(new OddIterator($list) as $p) echo $p;  // prints 13579
 foreach(new EvenIterator($list) as $p) echo $p; // prints 2468

ОБНОВЛЕНИЕ: php не имеет внутренних классов, так что вам здесь не повезло, по крайней мере не прибегая к eval. Ваши итераторы должны быть отдельными классами, которые осведомлены о структуре базового класса. Вы можете сделать его менее вредным, предоставив в базовом классе методы, которые будут создавать закулисные итераторы:

 class TreeDepthFirstIterator implements Iterator 
 {
      function __construct($someTree).....
 }


 class Tree
 {
       function depthFirst() { return new TreeDepthFirstIterator($this); }
        ....
 }


 foreach($myTree->depthFirst() as $node).....

Другой вариант - использовать лямбды вместо foreach. Это лучше и гибче, но требует php5.3:

 class Tree
 {
        function depthFirst($func) {
              while($node = .....)
                $func($node);

 .....

 $myTree->depthFirst(function($node) {
     echo $node->name;
 });
1 голос
/ 14 марта 2010

Для вашей цели может быть достаточно иметь флаг "mode" в вашем классе, чтобы пользователь мог выбрать, использовать ли итератор с первым или вторым по глубине значением.

class Tree {
  const TREE_DEPTH_FIRST = 0;
  const TREE_BREADTH_FIRST = 0;

  protected $mode;
  protected $current;

  public function __construct($mode=Tree::TREE_DEPTH_FIRST) {
    $this->mode = $mode;
  }

  public function setMode($mode) {
    ...
  }

  public function next() {
    $this->current = advance($this->current, $this->mode);
  }  
  ....
}

(и краткий ответ на ваш первоначальный вопрос: ни у одного php нет синтаксического сахара yield return, и у него нет внутренних закрытых классов, то есть того, что вам понадобится итератор, с которым вы возвращаетесь делать «оригинальный» объект должен быть представлен внешнему миру, так что вы, вероятно, в конечном итоге «подготовите» все элементов для объекта итератора, такого как ArrayIterator, а именно этого вы избегаете, используя yield)

1 голос
/ 14 марта 2010

Этот код показывает, как добавить несколько итераторов в класс.

class TreeNode {

public function getOddIterator () {
  return new OddIterator($this->nodes);
}

public function getEvenIterator () {
  return new EvenIterator($this->nodes);
}

}
0 голосов
/ 31 июля 2014

Вы можете иметь несколько итераторов. Основная идея Итератора состоит в том, чтобы взять на себя ответственность за доступ и обход объекта списка и поместить его в объект итератора. Так что если вы хотите иметь несколько итераторов с одним и тем же списком или разными списками; нет проблем.

Вы можете найти четыре различных примера PHP здесь:

http://www.php5dp.com/category/design-patterns/iterator/

Вы также можете использовать их со связанными списками.

Ура, Билл

...