Что именно возвращает PHP SPL RecursiveIterator::getChildren()?
Я изучаю стандартную библиотеку PHP (SPL) Маркуса Бергера.
Я реализовал свой собственный RecursiveIterator, который по наследству реализует Iterator
интерфейс. Это также реализует Countable
,
Меня смущают методы current (), getChildren () и hasChildren. Это задокументировано по адресу: http://www.php.net/~helly/php/ext/spl/interfaceRecursiveIterator.html
Если
current()
цитата: "Возвращает текущий элемент", иgetChildren()
возвращает, и я цитирую "суб итератор для текущего элемента"
Если, как в случае с current()
текущий элемент считается дочерним по отношению к текущему объекту.
Затем, безусловно, в документации указывается, что getChildren(), по сути, возвращает внуков рассматриваемого узла.
Следовательно, запутался.
<?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
объекты. Я думаю, что это форма Composite Pattern.
Благодаря экспериментам мне удалось выполнить рекурсивную операцию с деревом, используя 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)
это правильно?
Спецификации говорят об одном - что я понимаю буквально. Но мой практический опыт говорит другое.
1 ответ
Я не считаю правильным говорить "внуки". Вы просто меняете свою контрольную точку с элемента текущего итератора на текущий итератор, который превращает детей в внуков. Я не вижу веской причины для этого, потому что это просто не тот подход, к которому я привык с итераторами spl.
Я рекомендую вам придерживаться кода, подобного тому, который вы опубликовали, но я думаю, что вы, возможно, не знаете о RecursiveIteratorIterator. RecursiveIteratorIterator предназначен для того, чтобы справляться со сложностью вызова hasChildren() и getChildren() и поддерживать надлежащий стек итераторов в процессе. В конце концов, вам представляется то, что выглядит как плоский список вашей иерархии. Ваш класс NodeTreeProcessor в настоящее время делает некоторые из этих вещей. Затем вы можете просто перейти к RecursiveIteratorIterator, и вы получите ширину первой или глубины первой итерации в зависимости от того, какие флаги вы использовали. Вам не нужно использовать RecursiveIteratorIterator, хотя.
Также рассмотрите возможность возврата нового внешнего итератора при вызове getChildren(). В противном случае вы отказываетесь от возможности итерации по одному и тому же узлу с более чем одним итератором за раз, потому что позиция вашего итератора будет общим состоянием. В настоящее время вы используете внутреннюю парадигму итератора, в которой и данные, и состояние итерации хранятся в одном и том же объекте. Внешний итератор отсоединяет состояние итерации от данных, позволяя иметь более одного активного итератора на один и тот же фрагмент данных.