2010-03-14 2 views
4

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


Добрый день

Я знаю, что C# позволяет использовать несколько итераторов, используя выход, как описано здесь: Is Multiple Iterators is possible in c#?

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

Подробнее (EDIT):

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

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

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' 
     } 
    } 
} 
+0

Хороший материал, это хорошо, чтобы соединить знания по языкам Дев, это кажется. –

+1

Какую часть принятого ответа интересует? Часть, где класс «предлагает» разные итераторы или часть, где одновременно используются два итератора одного и того же объекта? Или оба? ;-) – VolkerK

+0

Мне интересен случай, когда класс предлагает разные итераторы – artvolk

ответ

5

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

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

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

На самом деле, до тех пор, как вы класс реализует итератор, вы можете применить любой произвольный 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 

UPDATE : php не имеет внутренних классов, поэтому вам здесь не повезло, не прибегая к eval, по крайней мере. Ваши итераторы должны быть отдельными классами, которые знают о структуре базового слоя. Вы можете сделать его менее вредным, предоставляя методы базового класса, создания экземпляров итераторов за кадром:

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


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


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

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

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

..... 

$myTree->depthFirst(function($node) { 
    echo $node->name; 
}); 
+0

Спасибо за объяснение, но если мне нужно перебрать более сложную структуру (например, дерево) Мне нужно изменить итерацию algorighm, а не только фильтровать некоторые элементы. Пожалуйста, см. Правки в оригинальное сообщение ... – artvolk

+0

Я опубликовал псевдо-код C#, чтобы уточнить ... – artvolk

1

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

class TreeNode { 

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

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

} 
1

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

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)

+0

Извините, кажется, я не могу отметить два сообщения в качестве принятого ответа, поэтому я отмечаю в своем сообщении, что ваше сообщение - это то, что я ищу тоже. – artvolk

0

У вас может быть несколько итераторов. Ключевая идея в Iterator заключается в том, чтобы взять на себя ответственность за доступ и обход объекта списка и поместить его в объект итератора. Поэтому, если вы хотите иметь несколько итераторов с одним и тем же списком или разными списками; нет проблем.

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

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

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

Приветствия, Билл

Смежные вопросы