在PHP中走树的最优雅方式

时间:2010-06-22 20:16:18

标签: php tree

我有这样的树:

$tree = array("A", array(
            array("B", 1),
            array("C", 2),
            array("D",
                array("E",
                    array("F")),
                array("G")),
            array("H", 3)));

每个节点都是一个数组,节点的类型是它的第一个元素,其他元素是节点的参数(它们可以是其他节点的列表,单个节点,某个值等;节点可以没有参数,一个参数或多个参数)。

您认为走这类树木最优雅的方式是什么?

我提出了两种可能性:

1)使用switch声明

/*
 * + shorter
 * + fall-througs (easy way to handle more nodes with same code)
 *
 * - worse readability
 */
function my_tree_walker($tree)
{
    switch ($tree[0]) {
        case 'A':
            list($_, $subnodes) = $tree;
            $ret = '';

            foreach ($subnodes as $subnode) {
                $ret .= my_tree_walker($subnode);
            }

            return $ret;

        break;
        case 'B': /*...*/ break;
        case 'C': /*...*/ break;
        case 'D': /*...*/ break;
        case 'E': /*...*/ break;
        case 'F': /*...*/ break;
        case 'G': /*...*/ break;
        case 'H': /*...*/ break;
    }
}

2)具有每种节点类型的方法的对象

/*
 * + better readability
 * + more declarative
 *
 * - longer
 * - `new static` is PHP >=5.3 only
 */

abstract class TreeWalker
{
    protected function __construct(){}

    final protected function walk($node)
    {
        $nodetype = array_shift($node);
        return call_user_func_array(array($this, 'walk' . $nodetype), $node);
    }

    public static function w($tree)
    {
        $instance = new static;
        return $instance->walk($tree);
    }
}

final class MyTreeWalker extends TreeWalker
{
    protected function __construct()
    {
        // initialize
    }

    private function walkA($subnodes)
    {
        $ret = '';

        foreach ($subnodes as $subnode) {
            $ret .= $this->walk($subnode);
        }

        return $ret;
    }

    private function walkB($n) { /*...*/ }
    private function walkC($n) { /*...*/ }
    private function walkD($subnode) { /*...*/ }
    private function walkE() { /*...*/ }
    private function walkF() { /*...*/ }
    private function walkG() { /*...*/ }
    private function walkH($n) { /*...*/ }
}

或者你建议更优雅的树木走路方式吗?

我还认为节点是对象,而不是单独的树步行者,每个节点都有内部漫游的方法。但是我认为它会使代码难以维护,因为步行者代码的某些部分将被放置在不同的位置,并且对于更多节点使用相同的代码将更加困难。

5 个答案:

答案 0 :(得分:3)

答案 1 :(得分:3)

我认为简约是优雅的。

无需重新发明轮子! PHP附带SPL (Standard PHP Library),它提供了几个可以为您完成所有工作的Iterators

一些要结帐的内容是RecursiveIteratorIteratorRecursiveArrayIterator

答案 2 :(得分:0)

我结合了两种方式并创建了DSL:

A ($subnodes) {
    $ret = '';
    foreach ($subnodes as $subnode) {
        $ret .= WALK($subnode);
    }

    return $ret;
}
B ($n) { /*...*/ }
C ($n) { /*...*/ }
D ($subnode) { /*...*/ }
E () { /*...*/ }
F () { /*...*/ }
G () { /*...*/ }
H ($n) { /*...*/ }

已翻译为PHP。

答案 3 :(得分:0)

我推荐这个库:https://packagist.org/packages/lukascivil/treewalker

TreeWalker是一个简单的小型库,可以帮助您更快地处理PHP中的结构

  • getdiff() - 获取json差异
  • walker() - 编辑json(递归)
  • structMerge() - 连接两个结构
  • createDynamicallyObjects() - 按动态键创建嵌套结构
  • getDynamicallyValue() - 动态获取结构属性
  • setDynamicallyValue() - 动态访问结构属性以设置值

答案 4 :(得分:0)

我做了一个简单的递归函数,可以有效地走树。在这里

   function deep_cetegories($categories){
     foreach($categories as $category)
  {
    print_r((json_encode($category['category_name'])));
    if(isset($category['children']))
    {
        deep_cetegories($category['children']);
    }

  }
}