【问题标题】:Are Multiple Iterators possible in php?php中是否可以使用多个迭代器?
【发布时间】:2010-03-14 11:24:38
【问题描述】:

请检查 VolkerK 的答案,他提供了另一种解决方案,但我无法将两个帖子标记为答案。 :(


美好的一天!

我知道 C# 允许使用 yield 的多个迭代器,如下所述: Is Multiple Iterators is possible in 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'
         }
     }
 }

【问题讨论】:

  • 好东西,它认为跨开发语言连接知识是件好事。
  • 您对已接受答案的哪一部分感兴趣?一个类“提供”不同迭代器的部分,还是同时使用同一对象的两个迭代器的部分?或两者? ;-)
  • 我对类提供不同迭代器的情况感兴趣
  • 您可能想解释一下yield return 的作用或给出一个示例用例

标签: php design-patterns iterator


【解决方案1】:

是的,你可以。

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

另一种选择是使用 lambdas 而不是 foreach。这更好,更灵活,但需要 php5.3:

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

 .....

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

【讨论】:

  • 感谢您的解释,但如果我需要迭代更复杂的结构(例如树),我需要更改迭代算法,而不仅仅是过滤一些元素。请查看对原始消息的修改...
【解决方案2】:

出于您的目的,在您的类中使用“模式”标志可能就足够了,因此用户可以选择是使用面包优先还是深度优先迭代器。

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 避免的事情)

【讨论】:

  • 抱歉,我似乎无法将两个帖子标记为已接受的答案,所以我在消息中指出,您的消息也是我正在寻找的。​​span>
【解决方案3】:

这段代码向您展示了如何在一个类中添加多个迭代器。

class TreeNode {

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

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

}

【讨论】:

    【解决方案4】:

    你可以有多个迭代器。 Iterator 中的关键思想是承担访问和遍历列表对象的责任,并将其放入迭代器对象中。因此,如果您想拥有多个具有相同列表或不同列表的迭代器;没问题。

    您可以在这里找到四个不同的 PHP 示例:

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

    您也可以将它们与链接列表一起使用。

    干杯, 比尔

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-08-01
      • 2020-03-04
      • 2016-09-01
      • 2015-04-01
      • 2019-12-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多