doubi8512 2013-07-03 03:53 采纳率: 0%
浏览 70

PHP SPL RecursiveIterator :: getChildren()究竟返回了什么?

I am learning about Marcus Boerger's Standard PHP Library (SPL).

I have implemented my own RecursiveIterator which, by inheritence, implements the Iterator interface. It also implements Countable.

I am confused by the current(), getChildren() and hasChildren methods. That are documented at: http://www.php.net/~helly/php/ext/spl/interfaceRecursiveIterator.html

If

  • current() quote: 'Returns the current element', and
  • getChildren() returns, and I quote, 'the sub iterator for the current element'

If, as is the case for current(), the current element is taken to mean the child of the current object.

Then, surely the documentation is specifying that getChildren(), in effect, returns the grandchildren of the node in question.

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. Because each RecursableCountableIterableNode object holds instances of other RecursableCountableIterableNode objects. 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.

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.

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,

  • getChildren really ought to return $this instead of the node at current(), and
  • hasChildren really ought to return the boolean cast result of count($this)

is this right?

The specs say one thing - which I take literally. But my practical experience says another.

  • 写回答

1条回答 默认 最新

  • douxie9347 2013-07-06 15:58
    关注

    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.

    I recomend you stick to code like what you posted, but I think maybe you're unaware of 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. 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. 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. You don't have to use RecursiveIteratorIterator though.

    Also, consider returning a new outer iterator when getChildren() is called. 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.

    评论

报告相同问题?

悬赏问题

  • ¥15 微信会员卡接入微信支付商户号收款
  • ¥15 如何获取烟草零售终端数据
  • ¥15 数学建模招标中位数问题
  • ¥15 phython路径名过长报错 不知道什么问题
  • ¥15 深度学习中模型转换该怎么实现
  • ¥15 HLs设计手写数字识别程序编译通不过
  • ¥15 Stata外部命令安装问题求帮助!
  • ¥15 从键盘随机输入A-H中的一串字符串,用七段数码管方法进行绘制。提交代码及运行截图。
  • ¥15 TYPCE母转母,插入认方向
  • ¥15 如何用python向钉钉机器人发送可以放大的图片?