PHP 中的嵌套数据集结构
Nested dataset structure in PHP
我正在尝试实现 nested dataset structure in PHP (another description of the structure) 的面向对象版本。我已经创建了一个节点实现:
class Node
{
private $parent;
private $nodes = [];
private $level = 1;
private $left = 1;
private $right = 2;
/**
* @return self|null
*/
public function getParent()
{
return $this->parent;
}
private function setParent(self $parent = null)
{
$this->parent = $parent;
}
public function getLevel(): int
{
return $this->level;
}
private function setLevel(int $level)
{
$this->level = $level;
}
public function getLeft(): int
{
return $this->left;
}
private function setLeft(int $left)
{
$this->left = $left;
}
public function getRight(): int
{
return $this->right;
}
private function setRight(int $right)
{
$this->right = $right;
}
/**
* @return static[]
*/
public function getNodes(): array
{
return $this->nodes;
}
public function addNode(Node $new)
{
$new->setLevel($this->getLevel() + 1);
$this->nodes[] = $new;
// @todo
}
}
但是我需要帮助来实现addNode
方法,它应该向当前节点添加一个新节点并更新整个树,尤其是新添加的节点,父节点、子节点等
为了让一切变得更简单,我创建了简单的测试用例,它将检查是否一切都已正确实施:
$country = new Node();
$state = new Node();
$city = new Node();
$country->addNode($state);
$state->addNode($city);
assert($country->getLeft() === 1);
assert($country->getRight() === 6);
assert($country->getLevel() === 1);
assert($state->getLeft() === 2);
assert($state->getRight() === 5);
assert($state->getLevel() === 2);
assert($city->getLeft() === 3);
assert($city->getRight() === 4);
assert($city->getLevel() === 3);
您不应该定义 parent 或对它的引用,因为您可能最终会陷入引用循环,这在代码中不是坏事,但在设计中可能最终让您出错时无法理解代码。
至于 addNode
功能,你似乎是在正确的轨道上,
public function addNode(Node $new)
{
//no need for getters and setters from inside the class
$new->level = $this->level + 1;
$this->nodes[] = $new;
return $this;
}
虽然我不确定您要用 left
和 right
做什么。但我认为它是顶部 parent 一直向左,底部 child 一直向右。在那种情况下,我只是将它与级别区分开来。
我也不会使用 private 而是使用 protected,我主要是在这里这样做,这样我就可以一直往上走,而不需要 getter 和 setter 来使代码更紧凑,但是按照你的感觉去做喜欢:
class Node
{
/**
* @var static
*/
protected $parent;
/**
* @var static[]
*/
protected $nodes = [];
/**
* @var int
*/
protected $level = 1;
/**
* @var int
*/
protected $left = 0;
/**
* @var int
*/
protected $right = 0;
public function addNode(Node $new)
{
$new->level = $this->level + 1;
$new->parent = $this;
$new->left = $new->level - 1;
if (empty($this->nodes)) {
$this->right = 1;
}
$curr = $this;
while (null !== $curr->parent) {
//walking up to the current parent and telling it there is a new level added
$curr->parent->right++;
//setting the current level 1 up
$curr = $curr->parent;
}
$this->nodes[] = $new;
return $this;
}
}
而且因为我 return $this
,我可以链接所有内容,或者像
一样嵌套 addNode 调用
$country = new Node();
$state = new Node();
$city = new Node();
$country->addNode($state->addNode($city));
经过一天的休息和一杯咖啡,终于完成了这项工作。这并不像看起来那么容易,因为添加新节点时必须重写整棵树:
public function addChild(Node $new): void
{
$this->nodes[] = $new;
$new->setParent($this);
$new->setLevel($this->getLevel() + 1);
$new->setLeft($this->getLeft() + 1);
$new->setRight($this->getLeft() + 2);
$rootNode = $this;
while (!empty($rootNode->getParent())) {
$rootNode = $this->getParent();
}
$rootNode->setLeft(1);
$this->updateTree($rootNode);
}
private function updateTree(Node $node): void
{
$startIndex = $node->getLeft();
foreach ($node->getChildren() as $child) {
$child->setLeft(++$startIndex);
$child->setRight(++$startIndex);
if (count($child->getChildren())) {
$this->updateTree($child);
$startIndex = $this->getLastChild($child)->getRight();
}
}
$node->setRight($this->getLastChild($node)->getRight() + 1);
}
private function getLastChild(Node $node): Node
{
return $node->getChildren()[count($node->getChildren()) - 1];
}
class 即将在 GitHub 上发布。
我正在尝试实现 nested dataset structure in PHP (another description of the structure) 的面向对象版本。我已经创建了一个节点实现:
class Node
{
private $parent;
private $nodes = [];
private $level = 1;
private $left = 1;
private $right = 2;
/**
* @return self|null
*/
public function getParent()
{
return $this->parent;
}
private function setParent(self $parent = null)
{
$this->parent = $parent;
}
public function getLevel(): int
{
return $this->level;
}
private function setLevel(int $level)
{
$this->level = $level;
}
public function getLeft(): int
{
return $this->left;
}
private function setLeft(int $left)
{
$this->left = $left;
}
public function getRight(): int
{
return $this->right;
}
private function setRight(int $right)
{
$this->right = $right;
}
/**
* @return static[]
*/
public function getNodes(): array
{
return $this->nodes;
}
public function addNode(Node $new)
{
$new->setLevel($this->getLevel() + 1);
$this->nodes[] = $new;
// @todo
}
}
但是我需要帮助来实现addNode
方法,它应该向当前节点添加一个新节点并更新整个树,尤其是新添加的节点,父节点、子节点等
为了让一切变得更简单,我创建了简单的测试用例,它将检查是否一切都已正确实施:
$country = new Node();
$state = new Node();
$city = new Node();
$country->addNode($state);
$state->addNode($city);
assert($country->getLeft() === 1);
assert($country->getRight() === 6);
assert($country->getLevel() === 1);
assert($state->getLeft() === 2);
assert($state->getRight() === 5);
assert($state->getLevel() === 2);
assert($city->getLeft() === 3);
assert($city->getRight() === 4);
assert($city->getLevel() === 3);
您不应该定义 parent 或对它的引用,因为您可能最终会陷入引用循环,这在代码中不是坏事,但在设计中可能最终让您出错时无法理解代码。
至于 addNode
功能,你似乎是在正确的轨道上,
public function addNode(Node $new)
{
//no need for getters and setters from inside the class
$new->level = $this->level + 1;
$this->nodes[] = $new;
return $this;
}
虽然我不确定您要用 left
和 right
做什么。但我认为它是顶部 parent 一直向左,底部 child 一直向右。在那种情况下,我只是将它与级别区分开来。
我也不会使用 private 而是使用 protected,我主要是在这里这样做,这样我就可以一直往上走,而不需要 getter 和 setter 来使代码更紧凑,但是按照你的感觉去做喜欢:
class Node
{
/**
* @var static
*/
protected $parent;
/**
* @var static[]
*/
protected $nodes = [];
/**
* @var int
*/
protected $level = 1;
/**
* @var int
*/
protected $left = 0;
/**
* @var int
*/
protected $right = 0;
public function addNode(Node $new)
{
$new->level = $this->level + 1;
$new->parent = $this;
$new->left = $new->level - 1;
if (empty($this->nodes)) {
$this->right = 1;
}
$curr = $this;
while (null !== $curr->parent) {
//walking up to the current parent and telling it there is a new level added
$curr->parent->right++;
//setting the current level 1 up
$curr = $curr->parent;
}
$this->nodes[] = $new;
return $this;
}
}
而且因为我 return $this
,我可以链接所有内容,或者像
$country = new Node();
$state = new Node();
$city = new Node();
$country->addNode($state->addNode($city));
经过一天的休息和一杯咖啡,终于完成了这项工作。这并不像看起来那么容易,因为添加新节点时必须重写整棵树:
public function addChild(Node $new): void
{
$this->nodes[] = $new;
$new->setParent($this);
$new->setLevel($this->getLevel() + 1);
$new->setLeft($this->getLeft() + 1);
$new->setRight($this->getLeft() + 2);
$rootNode = $this;
while (!empty($rootNode->getParent())) {
$rootNode = $this->getParent();
}
$rootNode->setLeft(1);
$this->updateTree($rootNode);
}
private function updateTree(Node $node): void
{
$startIndex = $node->getLeft();
foreach ($node->getChildren() as $child) {
$child->setLeft(++$startIndex);
$child->setRight(++$startIndex);
if (count($child->getChildren())) {
$this->updateTree($child);
$startIndex = $this->getLastChild($child)->getRight();
}
}
$node->setRight($this->getLastChild($node)->getRight() + 1);
}
private function getLastChild(Node $node): Node
{
return $node->getChildren()[count($node->getChildren()) - 1];
}
class 即将在 GitHub 上发布。