4

PLEASE CHECK ANSWERS by VolkerK too, he provided another solution, but I can't mark two posts as answer. :(


Good day!

I know that C# allows multiple iterators using yield, like described here: Is Multiple Iterators is possible in c#?

In PHP there is and Iterator interface. Is it possible to implement more than one iteration scenario for a class?

More details (EDIT):

For example I have class TreeNode implementing single tree node. The whole tree can be expressed using only one this class. I want to provide iterators for iterating all direct and indirect children of current node, for example using BreadthFirst or DepthFirst order.

I can implement this Iterators as separate classes but doing so I need that tree node should expose it's children collection as public.

C# pseudocode:

 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
  • Good stuff, it's good to connect knowledge across dev languages, it think. Commented Mar 14, 2010 at 11:45
  • 1
    Which part of the accepted answer are you interested in? The part where a class "offers" different iterators or the part where two iterators of the same object are used simultaneously? Or both? ;-) Commented Mar 14, 2010 at 12:18
  • I interested in case when class offers different iterators Commented Mar 14, 2010 at 12:42
  • You might want to explain what yield return does or give an example usecase Commented Mar 14, 2010 at 13:21

4 Answers 4

5

Yes, you can.

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

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

Actually, as long as you class implements Iterator, you can apply any arbitrary IteratorIterator to it. This is a Good Thing, because applied meta iterators are not required to know anything about the class in question.

Consider, for example, an iterable class like this

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; }
}

Let's define some meta iterators

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

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

Now apply meta iterators to the base class:

 $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 has no inner classes, so you're kinda out of luck here, without resorting to eval, at least. Your iterators need to be separate classes, which are aware of the baseclass structure. You can make it less harmful by providing methods in the base class that instantiate iterators behind the scenes:

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


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


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

Another option is to use lambdas instead of foreach. This is nicer and more flexible, requires php5.3 though:

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

 .....

 $myTree->depthFirst(function($node) {
     echo $node->name;
 });
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for explanation, but if I need to iterate over more complex structure (for example tree) I need to change iteration algorighm, not only filter some elements. Please see edits to original message...
1

For your purpose it might be sufficient to have a "mode" flag in your class, so the user can choose whether to have a bread-first or a depth-first iterator.

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);
  }  
  ....
}

(and the short answer to your initial question: no php doesn't have the syntactic sugar of yield return and it doesn't have inner private classes, i.e. whatever you would need the iterator you're returning to do with the "original" object has to be exposed to the outside world. So you'd probably end up "preparing" all elements for an iterator object like ArrayIterator, the very thing you avoid by using yield)

1 Comment

Sorry, it seems I can't mark two posts as accepted answer, so I note in my message that your message is what I looking for too.
1

This code shows you how to add multiple iterators in a class.

class TreeNode {

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

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

}

Comments

0

You can have multiple Iterators. The key idea in the Iterator is to take the responsibility for access and traversal out of the list object and put it into an iterator object. So if you want to have multiple Iterators with the same list or different lists; there's no problem.

You can find four different PHP examples here:

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

You can also use them with Linked Lists.

Cheers, Bill

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.