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

Posted

技术标签:

【中文标题】PHP SPL RecursiveIterator::getChildren() 究竟返回了啥?【英文标题】:What exactly does PHP SPL RecursiveIterator::getChildren() return?PHP SPL RecursiveIterator::getChildren() 究竟返回了什么? 【发布时间】:2013-06-30 15:13:40 【问题描述】:

我正在学习 Marcus Boerger 的标准 php 库 (SPL)。

我已经实现了我自己的 RecursiveIterator,它通过继承实现了Iterator 接口。它还实现了Countable

我对@9​​87654321@、getChildren() 和hasChildren 方法感到困惑。记录在:http://www.php.net/~helly/php/ext/spl/interfaceRecursiveIterator.html

如果

current() quote: '返回当前元素', getChildren() 返回,我引用,“当前元素的子迭代器”

如果像current() 的情况一样,当前元素 被认为是当前对象的子元素。

然后,文档肯定会指定 getChildren() 实际上返回相关节点的 grandchildren

因此感到困惑。

<?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 ] );
           

    

在上面的类中,getChildren() 返回一个实现 RecursiveIterator 和 Countable 的对象。因为每个RecursableCountableIterableNode 对象都包含其他RecursableCountableIterableNode 对象的实例。我认为它是一种复合模式。

通过实验,我设法通过使用count()(作为退出递归过程的终止条件)和foreach 来迭代每个节点的子节点,从而对树执行递归操作。

有趣的是,实际上,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 );
                   
        
    

鉴于此,我认为为了成为一个实用的 RecursiveIterator,

getChildren 真的应该返回 $this 而不是 current() 的节点,并且 hasChildren 真的应该返回 count($this) 的布尔转换结果

是这样吗?

规格说明了一件事 - 我从字面上理解。但我的实践经验说明了另一个问题。

【问题讨论】:

getChildren 应该返回包裹在另一个迭代器中的 current()。如果它有孩子 感谢您的评论。它确实返回 current() - 但没有像你说的那样包装。我应该澄清getChildren() 确实返回了一个实现RecursiveIterator 和Countable 的对象。每个RecursableCountableIterableNode 对象都包含其他RecursableCountableIterableNode 对象的实例。我认为它是一种复合模式。我将编辑问题以澄清。 @tlens 根据我上面的评论,是否有任何理由将其包装在另一个迭代器中? 所以你可以以真正的“递归”方式迭代它们 【参考方案1】:

我认为说“孙子”是不对的。您只是将您的参考点从当前迭代器的 element 更改为 当前迭代器,这会使子代变为大子代。我认为没有充分的理由这样做,因为这不是我习惯使用 spl 迭代器的约定。

我建议您坚持使用您发布的代码,但我想您可能不知道 RecursiveIteratorIterator。 RecursiveIteratorIterator 旨在处理调用 hasChildren() 和 getChildren() 的复杂性,并在此过程中维护适当的迭代器堆栈。最后,您会看到似乎是您的层次结构的扁平列表。您的 NodeTreeProcessor 类目前正在做一些这样的事情。然后,您可以在 RecursiveIteratorIterator 上进行 foreach,根据您使用的标志,您将获得广度优先或深度优先迭代。不过,您必须使用 RecursiveIteratorIterator。

另外,考虑在调用 getChildren() 时返回一个新的外部迭代器。否则,您将放弃一次使用多个迭代器迭代同一节点的可能性,因为您的迭代器的位置将是共享状态。目前,您使用的是 inner iterator 范例,其中数据和迭代状态都存储在同一个对象中。外部迭代器将迭代状态与数据分离,让您在同一条数据上拥有超过 1 个活动迭代器。

【讨论】:

感谢您的回答。非常有用的信息。

以上是关于PHP SPL RecursiveIterator::getChildren() 究竟返回了啥?的主要内容,如果未能解决你的问题,请参考以下文章

php spl库的使用

PHP、SPL、ArrayAccess 接口

PHP SPL使用方法 自动加载和迭代器

夯实PHP基础PHP标准库 SPL

PHP的SPL扩展库函数

PHP标准库 SPL