marcini82: najpierw myslalem ze chodzi ci o rekurencje w samym php, a nie przy pobieraniu parentow z bazy, tak?
<pre><?php
array( 'id' => 1, 'parent_id' => null, 'name' => '1' ), array( 'id' => 3, 'parent_id' => null, 'name' => '3' ), array( 'id' => 2, 'parent_id' => null, 'name' => '2' ),
array( 'id' => 4, 'parent_id' => 1, 'name' => '1_1' ), array( 'id' => 5, 'parent_id' => 1, 'name' => '1_2' ), array( 'id' => 6, 'parent_id' => 1, 'name' => '1_3' ),
array( 'id' => 7, 'parent_id' => 2, 'name' => '2_1' ), array( 'id' => 8, 'parent_id' => 2, 'name' => '2_2' ),
array( 'id' => 9, 'parent_id' => 3, 'name' => '3_1' ) );
$treeObject = createTreeObject( $records );
printTree( $treeObject );
function createTreeObject( $records )
{
$nodeMap = new NodeMap();
foreach ( $records as $record )
{
$nodeMap->addNode( new Node( $record['id'], $record['name'], $record['parent_id'] ) );
}
return new Tree( $nodeMap->getTopLevelNodes() );
}
function printTree( $tree )
{
$tree->printMe();
}
class Tree
{
private $topLevelNodes = array();
public function __construct( $nodes )
{
$this->topLevelNodes = $nodes;
}
public function printMe()
{
foreach ( $this->topLevelNodes as $node )
{
$this->printNode( $node, 0 );
}
}
// rekurencja !!!
private function printNode( $node, $level )
{
$str = "+" . str_repeat( '-', $level + 1 ) . " " . $node->getName();
foreach ( $node->getChildren() as $childNode )
{
$this->printNode( $childNode, $level + 1 );
}
}
}
class Node
{
private $children = array(); private $id;
private $name;
private $parentId;
public function __construct( $id, $name, $parentId )
{
$this->id = $id;
$this->name = $name;
$this->parentId = $parentId;
}
public function addChild( Node $child )
{
$this->children[] = $child;
}
public function getChildren()
{
return $this->children;
}
public function getName()
{
return $this->name;
}
public function getParentId()
{
return $this->parentId;
}
public function getId()
{
return $this->id;
}
}
class NodeMap
{
private $nodes = array();
private $nodesWaitingToBeAdded = array();
public function addNode( $node )
{
$this->nodes[ $node->getId() ] = $node;
$this->addToParentNode( $node );
$this->addWaitingChildren( $node );
}
private function addToParentNode( $node )
{
$parent = $this->findParent( $node );
if ( $parent )
{
$parent->addChild( $node );
}
else
{
$this->markWaitingForParent( $node );
}
}
private function findParent( $node )
{
$parentId = $node->getParentId();
$parentExists = isset( $this->nodes[ $parentId ] ); if ( $parentExists )
{
return $this->nodes[ $parentId ];
}
else
{
return null;
}
}
private function markWaitingForParent( $node )
{
$thereIsArrayForNodesToBeAdded = isset( $this->nodesWaitingToBeAdded[ $node->getParentId() ] ); if ( $thereIsArrayForNodesToBeAdded )
{
// add next item
$this->nodesWaitingToBeAdded[ $node->getParentId() ][] = $node;
}
else
{
// create new array
$this->nodesWaitingToBeAdded[ $node->getParentId() ] = array( $node ); }
}
private function addWaitingChildren( $node )
{
$thereAreNodesToBeAdded = isset( $this->nodesWaitingToBeAdded[ $node->getId() ] ); if ( $thereAreNodesToBeAdded )
{
$children = $this->nodesWaitingToBeAdded[ $node->getId() ];
foreach ( $children as $child )
{
$node->addChild( $child );
}
}
// remove these, so the only one left will be top level nodes
unset( $this->nodesWaitingToBeAdded[ $node->getId() ] ); }
public function getTopLevelNodes()
{
return $this->nodesWaitingToBeAdded[''];
}
}
?>
</pre>
Co to robi? Przerabia rekordy na drzewko Node'ow i drukuje je. Zachowuje kolejnosc rekordow z bazy, tzn jesli 1_2 bylo przed 1_1 to tak zostana wyswietlone
Niestety jest rekurencja (bo nie chcialo mi sie rozpisywac) O takie cos chodzilo?
Zeby pozbyc sie tej rekurencji trzeba zasymulowac zachowanie stosu przy korzystaniu z rekurencji. Chyba zaraz to napisze

printMe // rekurencja
printMeWithUseOfIterations // iteracja, pewnie da sie przyspieszyc, ale juz mi sie nie chce

<pre><?php
array( 'id' => 1, 'parent_id' => null, 'name' => '1' ), array( 'id' => 3, 'parent_id' => null, 'name' => '3' ), array( 'id' => 2, 'parent_id' => null, 'name' => '2' ),
array( 'id' => 4, 'parent_id' => 1, 'name' => '1_1' ), array( 'id' => 5, 'parent_id' => 1, 'name' => '1_2' ), array( 'id' => 6, 'parent_id' => 1, 'name' => '1_3' ),
array( 'id' => 7, 'parent_id' => 2, 'name' => '2_1' ), array( 'id' => 8, 'parent_id' => 2, 'name' => '2_2' ),
array( 'id' => 9, 'parent_id' => 3, 'name' => '3_1' ) );
/*
glebokie drzewko
$records = array();
$item = array('id' => 1, 'parent_id' => null, 'name' => '1' );
for ( $j = 2; $j < DEPTH; $j++ )
{
$records[] = array( 'id' => $j, 'parent_id' => ($j - 1), 'name' => 'blah' );
}
*/
$treeObject = createTreeObject( $records );
printTree( $treeObject );
function createTreeObject( $records )
{
$nodeMap = new NodeMap();
foreach ( $records as $record )
{
$nodeMap->addNode( new Node( $record['id'], $record['name'], $record['parent_id'] ) );
}
return new Tree( $nodeMap->getTopLevelNodes() );
}
function printTree( $tree )
{
benchmark( $tree, 'printMe' );
benchmark( $tree, 'printMeWithUseOfIterations' );
}
function benchmark( $tree, $func )
{
for ( $i = 0; $i < REPEAT; $i++ )
{
$tree->$func();
}
print( "$func: " . ( $end - $start ) . "\n" ); }
class Tree
{
private $topLevelNodes = array();
public function __construct( $nodes )
{
$this->topLevelNodes = $nodes;
}
public function printMeWithUseOfIterations()
{
$currentProcessedNodes = array();
$level = 0;
// on extract -> increase?
? while ( ! ( empty( $todoNodes ) && empty( $currentProcessedNodes ) ) ) {
if ( empty( $currentProcessedNodes ) ) {
// remove node from TODO and return it
$currentProcessedNodes[] = $firstNodeTodo;
}
$nextNode = array_pop( $currentProcessedNodes ); // the last one: remove and return
//print($nextNode->getName() . "\n" );
$currentProcessedNodes = array_merge( $currentProcessedNodes, $reversedChildren ); }
}
public function printMe()
{
foreach ( $this->topLevelNodes as $node )
{
$this->printNode( $node, 0 );
}
}
// rekurencja !!!
private function printNode( $node, $level )
{
$str = "+" . str_repeat( '-', $level + 1 ) . " " . $node->getName(); //print( $str . "\n" );
foreach ( $node->getChildren() as $childNode )
{
$this->printNode( $childNode, $level + 1 );
}
}
}
class Node
{
private $children = array(); private $id;
private $name;
private $parentId;
public function __construct( $id, $name, $parentId )
{
$this->id = $id;
$this->name = $name;
$this->parentId = $parentId;
}
public function addChild( Node $child )
{
$this->children[] = $child;
}
public function getChildren()
{
return $this->children;
}
public function getName()
{
return $this->name;
}
public function getParentId()
{
return $this->parentId;
}
public function getId()
{
return $this->id;
}
}
class NodeMap
{
private $nodes = array();
private $nodesWaitingToBeAdded = array();
public function addNode( $node )
{
$this->nodes[ $node->getId() ] = $node;
$this->addToParentNode( $node );
$this->addWaitingChildren( $node );
}
private function addToParentNode( $node )
{
$parent = $this->findParent( $node );
if ( $parent )
{
$parent->addChild( $node );
}
else
{
$this->markWaitingForParent( $node );
}
}
private function findParent( $node )
{
$parentId = $node->getParentId();
$parentExists = isset( $this->nodes[ $parentId ] ); if ( $parentExists )
{
return $this->nodes[ $parentId ];
}
else
{
return null;
}
}
private function markWaitingForParent( $node )
{
$thereIsArrayForNodesToBeAdded = isset( $this->nodesWaitingToBeAdded[ $node->getParentId() ] ); if ( $thereIsArrayForNodesToBeAdded )
{
// add next item
$this->nodesWaitingToBeAdded[ $node->getParentId() ][] = $node;
}
else
{
// create new array
$this->nodesWaitingToBeAdded[ $node->getParentId() ] = array( $node ); }
}
private function addWaitingChildren( $node )
{
$thereAreNodesToBeAdded = isset( $this->nodesWaitingToBeAdded[ $node->getId() ] ); if ( $thereAreNodesToBeAdded )
{
$children = $this->nodesWaitingToBeAdded[ $node->getId() ];
foreach ( $children as $child )
{
$node->addChild( $child );
}
}
// remove these, so the only one left will be top level nodes
unset( $this->nodesWaitingToBeAdded[ $node->getId() ] ); }
public function getTopLevelNodes()
{
foreach ( $this->nodes as $node )
{
if ( is_null( $node->getParentId() ) ) {
$nodes[] = $node;
}
}
return $nodes;
}
}
?>
</pre>
Wyszlo mi
dla glebokiego drzewka :
define( 'DEPTH', 10000); define( 'REPEAT', 100000);
printMe: 0.088973999023438
printMeWithUseOfIterations: 0.26687598228455
rekurencja rzadzi ?

dla "szerokiego"
define( 'REPEAT', 100000);
printMe: 3.2444250583649
printMeWithUseOfIterations: 3.3689439296722
bez roznicy