简体   繁体   English

PHP SPL RecursiveIterator :: getChildren()到底返回什么?

[英]What exactly does PHP SPL RecursiveIterator::getChildren() return?

I am learning about Marcus Boerger's Standard PHP Library (SPL). 我正在学习Marcus Boerger的标准PHP库(SPL)。

I have implemented my own RecursiveIterator which, by inheritence, implements the Iterator interface. 我已经实现了自己的RecursiveIterator,通过继承它实现了Iterator接口。 It also implements Countable . 它还实现Countable

I am confused by the current() , getChildren() and hasChildren methods. 我对current()getChildren()hasChildren方法感到困惑。 That are documented at: http://www.php.net/~helly/php/ext/spl/interfaceRecursiveIterator.html 记录在: http : //www.php.net/~helly/php/ext/spl/interfaceRecursiveIterator.html

If 如果

  • current() quote: 'Returns the current element ', and current() quote:'返回当前元素 ',并且
  • getChildren() returns, and I quote, 'the sub iterator for the current element ' getChildren()返回,并引用“ 当前元素的子迭代器”

If, as is the case for current() , the current element is taken to mean the child of the current object. 如果与current()的情况一样, 则将当前元素视为当前对象的子代。

Then, surely the documentation is specifying that getChildren(), in effect, returns the grandchildren of the node in question. 然后,肯定是文档指定了getChildren()实际上会返回所涉及节点的代。

Hence confused. 因此感到困惑。

<?php

/**
*@desc Represents a node within a hierarchy
*/
class RecursableCountableIterableNode implements RecursiveIterator, Countable
    {

    public $title;

    private $_componentsArray;
    private $_iteratorPosition;

    /**
    *@desc adds component
    */
    public function addComponent( 
            RecursableCountableIterableNode $incomingNodeObj 
            )
        {

        foreach ( $this->_componentsArray as $componentNodeObj )
            {
            if ( $incomingNodeObj === $componentNodeObj )
                {
                //its is already in there
                return;
                }
            }


        //add to the next element of the numerically indexed array
        $this->_componentsArray[] = $incomingNodeObj;       
        }



    /**
    * @desc RecursiveIterator Interface 
    */

    /**
    * @desc Implements the RecursiveIterator Interface 
    * @return boolean - Whether or not the node at the current element
    *  has children.
    * 
    * Note: This method does NOT count the children of this node, 
    * it counts the components of the node at the *current* element.
    * There is a subtle but important difference. 
    * It could have been better to name 
    * the interface method 'hasGrandChildren()'.
    */
    public function hasChildren()
        {
        return ( boolean ) count( $this->current() );
        }

    /**
    * @desc Gets the node of the current element which in effect is a container
    *  for childnodes. 
    * 
    * Note: According to the SPL, it does NOT get 'the child elements of
    *  the current node' ($this->_componentsArray) which was a surprise to me.
    * 
    * @return RecursableCountableIterableNode - the 
    * sub iterator for the current element 
    * 
    */
    public function getChildren()
        {
        return $this->current();
        }


    /**
    * @desc To adhere to countable interface.
    * @returns integer - The number of elements in the compondents array.
    */
    public function count()
        {
        return count( $this->_componentsArray );
        }


    /**
    * Iterator methods
    */

    /**
    * @desc Rewind the iterator to the first element.
    * @return void
    */
    public function rewind()
        {
        $this->_iteratorPosition = 0;
        }

    /**
    * @desc Return the current element.
    * @return RecursableCountableIterableNode
    */
    public function current()
        {
        return $this->_componentsArray[ $this->_iteratorPosition ];
        }

    /**
    * @desc Return the key of the current element.
    * @return integer
    */
    public function key()
        {
        return $this->_iteratorPosition;
        }

    /**
    * @desc Move forward to the next element.
    * @return void
    */
    public function next()
        {
        ++$this->_iteratorPosition;
        }

    /**
    * @desc Checks if current position has an element
    * @return boolean
    */
    public function valid()
        {
        return isset( $this->_componentsArray[ $this->_iteratorPosition ] );
        }   

    }

In the class above, getChildren() returns an object that implements RecursiveIterator and Countable. 在上面的类中, getChildren()返回一个实现RecursiveIterator和Countable的对象。 Because each RecursableCountableIterableNode object holds instances of other RecursableCountableIterableNode objects. 因为每个RecursableCountableIterableNode对象都包含其他RecursableCountableIterableNode对象的实例。 I think its a form of Composite pattern. 我认为这是一种复合模式。

Through experimentation I have managed to perform a recursive operation on the tree by using count() (as a terminal condition to exit the recursive process) and foreach to iterate over each node's children. 通过实验,我设法通过使用count() (作为退出递归过程的最终条件count()对树执行递归操作,并让foreach遍历每个节点的子代。

What's interesting is that, in effect, the count feature implicity does a hasChildren operation and the foreach construct is implicitly doing a getChildren operation in order to perform recursive traversal. 有趣的是,实际上, count功能隐式地执行了hasChildren操作,而foreach构造函数隐式地执行了getChildren操作,以执行递归遍历。

class NodeTreeProcessor
    {
    protected $output = '';

    public function doProcessingWithNode( 
            RecursableCountableIterableNode $treeNodeObj
            )
        {

        $this->output .= $treeNodeObj->title;

        //Base case that stops the recursion.
        if (!( count( $treeNodeObj ) > 0 ))
            {
            //it has no children
            return;
            }

        //Recursive case.
        foreach( $treeNodeObj as $childNode )
            {
            $this->doProcessingWithNode( $childNode );
            }       
        }
    }

Given that, I am thinking that in order to be a practical RecursiveIterator, 鉴于此,我认为为了成为一个实用的RecursiveIterator,

  • getChildren really ought to return $this instead of the node at current() , and getChildren实际上应该返回$this而不是current()处的节点,并且
  • hasChildren really ought to return the boolean cast result of count($this) hasChildren确实应该返回count($this)的布尔值转换结果

is this right? 这是正确的吗?

The specs say one thing - which I take literally. 规范说了一件事-我从字面上理解。 But my practical experience says another. 但是我的实际经验又说一遍。

I don't think its right to say "grand children". 我认为说“大孩子”没有权利。 You're just changing your reference point from the an element of current iterator, to the current iterator, which makes the children into grand children. 你只是改变你的参考点从当前迭代的元素当前迭代器,这使得孩子到孙子女。 I don't see a good reason to do that, because it's just not the convention I'm accustomed to with spl iterators. 我看不出这样做的充分理由,因为这不是我习惯于spl迭代器的约定。

I recomend you stick to code like what you posted, but I think maybe you're unaware of RecursiveIteratorIterator . 我建议您坚持使用与您发布的代码相同的代码,但是我认为您可能并不了解RecursiveIteratorIterator A RecursiveIteratorIterator is meant to be a thing that will handle the complexity of calling hasChildren() and getChildren(), and maintain a proper stack of iterators in the process. RecursiveIteratorIterator意味着可以处理调用hasChildren()和getChildren()的复杂性,并在过程中维护适当的迭代器堆栈。 In then end, you're presented with what appears to be a flattened list of your hierarchy. 最后,您看到的是层次结构的扁平列表。 Your NodeTreeProcessor class is currently doing some of this stuff. 您的NodeTreeProcessor类当前正在做这些事情。 Then you can just foreach over the RecursiveIteratorIterator, and you'll get a breadth first or depth first iteration depending on what flags you used. 然后,您可以只遍历RecursiveIteratorIterator,然后根据所使用的标志获得广度优先或深度优先的迭代。 You don't have to use RecursiveIteratorIterator though. 不必使用虽然RecursiveIteratorIterator。

Also, consider returning a new outer iterator when getChildren() is called. 另外,考虑在调用getChildren()时返回一个新的外部迭代器 Otherwise, you forgo the possibility of iterating over the same node with more than one iterator at a time, because the position of your iterator will be shared state. 否则,您将放弃一次在同一节点上使用多个迭代器进行迭代的可能性,因为迭代器的位置将处于共享状态。 Currently, you're using the inner iterator paradigm, where both the data, and the state of iteration is stored in the same object. 当前,您正在使用内部迭代器范例,其中数据和迭代状态都存储在同一对象中。 An outer iterator detaches the the iteration state from the data, letting you have more than 1 active iterator over the same piece of data. 外部迭代器从数据中分离迭代状态,从而使您可以在同一数据段上拥有多个活动迭代器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM