【问题标题】:Recursion and passing by reference递归和引用传递
【发布时间】:2011-05-18 09:12:03
【问题描述】:

我有一个具有以下结构的类别树:

[6] => Array
    (
        [id] => 6
        [name] => computers
        [productCount] => 0
        [children] => Array
            (
                [91] => Array
                    (
                        [id] => 91
                        [name] => notebook
                        [productCount] => 5
                        [children] => Array
                            (
                            )
                    )

                [86] => Array
                    (
                        [id] => 86
                        [name] => desktop
                        [productCount] => 0
                        [children] => Array
                            (
                            )
                    )
            )
    )

除了子类别之外,每个类别都可能包含产品(就像一个文件夹可能包含子文件夹而仅包含文件)。

我正在尝试编写一个递归函数,我想将此数组作为参考,并剥离具有 [productCount] = 0 的叶类别和包含此类空节点的所有父类别。换句话说,在处理之后,我只想拥有那些在任何子级别上包含产品的类别。

我已经写了一些代码,现在正在调试它,它不会剥离空节点。可能是我没有正确使用参考。如果可能,请帮我解决它。

    function pruneTree( & $node) {
    if ( ! $node['children'] && ! $node['productCount']) {
        unset($node);
    }
    if ( ! empty($node['children'])) {
        foreach ($node['children'] as $key => $child) {
            pruneTree($node['children'][$key]);
        }
    }
    return;
}

【问题讨论】:

  • @Ghommey:是的,在 PHP 中,空数组被认为是虚假的。

标签: php recursion pass-by-reference


【解决方案1】:

我会那样做。注意 foreach 中的“&”。

function pruneTree(&$node)
{
    foreach ($node as $index => &$value) {
        if (empty($value)) {
            unset($node[$index]);
        } elseif (is_array($value)) {
            pruneTree($value);
        }
    }
}

【讨论】:

  • 所有其他答案,即使是接受的答案,都已经告诉我们了。这里有什么新东西吗?
【解决方案2】:

我不知道是不是这样,但是当我需要在数组中递归更改值时,我也需要将 & 传递给 foreach 值。

private function convertXMLPart(&$array) {
        foreach ($array as $rowKey => &$row) {
            if (gettype($row) != 'string') {
                $row = (array)$row;
                if (!empty($row['@attributes'])) {
                    foreach ($row['@attributes'] as $key => $value) {
                        $row[$key] = $value;
                    }
                    unset($row['@attributes']);
                    $array[$rowKey] = $row;
                }
                $this->convertXMLPart($row);
            }
        }
    }

【讨论】:

    【解决方案3】:

    您还可以更改函数中的参数以获取节点数组而不是单个节点。这会稍微改变递归,并避免传递密钥:

    function pruneTree(&$nodes) {
        foreach ($nodes as $key => $node) {
            if (!$node['children'] && !$node['productCount']) {
                unset($nodes[$key]);
            } elseif (!empty($node['children'])) {
                pruneTree($nodes[$key]['children']);
                // This line checks if all the children have been pruned away:
                if (empty($nodes[$key]['children'])) {
                    unset($nodes[$key]);
                }
            }
        }
    }
    

    此外,添加了一项检查,以确保如果所有子节点都被修剪,则父(现在,叶)节点也会被修剪。

    希望这会有所帮助!


    测试数据:

    $data = array(
        6 => array(
            'id' => 6,
            'name' => 'computers',
            'productCount' => 0,
            'children' => array(
                91 => array(
                    'id' => 91,
                    'name' => 'notebook',
                    'productCount' => 5,
                    'children' => array()
                ),
                86 => array(
                    'id' => 86,
                    'name' => 'desktop',
                    'productCount' => 0,
                    'children' => array()
                )
            )
        )
    );
    

    召唤:

    pruneTree($data);
    echo '<pre>';
    print_r($data);
    echo '</pre>';
    

    【讨论】:

    • 事实证明不可能在函数内部使用unset($nodes[$key]); 来修改通过引用传递的初始数组,因为它只会取消设置函数范围内的引用变量。
    • @sevenWonders - 我忘了提到我测试了这个脚本(以及 Gumbo 的),它们都可以工作。几乎没有区别,只是我在取消设置之前从被调用函数中找到了密钥。
    • @RabidFire - 奇怪的是,当我测试你的函数时,我在pruneTree($nodes[$key]['children']); 行收到错误“只能通过引用传递变量”。
    • 添加了测试数据和我调用它的方式。我的 PHP 版本是 5.3.0。也许与此有关。不知道,让我难住了。我不能通过引用传入变量并从函数中取消设置它,除非它是一个数组并且我有密钥。
    • @RabidFire - 哦,很抱歉,在我的应用程序中测试您的代码时,我忽略了您的函数采用节点数组而不是单个节点的事实。我的错。您的代码完美无缺!而且它更适合我的应用程序,因为用于修剪的树实际上来自另一个数组,由于我最初的递归设计,我不得不在循环中迭代它,而您的方法也简化了这一点。
    【解决方案4】:

    unset 只删除引用而不删除被引用的变量:

    如果通过引用传递的变量是函数内部的unset(),则只有局部变量被销毁。调用环境中的变量将保留与调用 unset() 之前相同的值。

    所以你需要传递父数组和删除该变量的键:

    function pruneTree(&$parent, $key) {
        $node = &$parent[$key];
        if (!$node['children'] && !$node['productCount']) {
            unset($parent[$key]);
        }
        if (!empty($node['children'])) {
            foreach ($node['children'] as $key => &$child) {
                pruneTree($node['children'], $key);
            }
        }
    }
    

    【讨论】:

    • 谢谢你,秋葵!我一直在手册的“参考”页面上寻找线索,并错过了unset 和范围。
    猜你喜欢
    • 2012-10-10
    • 1970-01-01
    • 2019-11-11
    • 2013-07-22
    • 2011-05-02
    • 2020-05-18
    • 2020-10-02
    • 2011-12-12
    • 1970-01-01
    相关资源
    最近更新 更多