Perl:从递归调用中捕获数据

时间:2018-10-15 21:10:30

标签: perl recursion

我编写了一个函数来接收数组数组,并以XML格式将其打印出来,但不带标签。当我保持Buffer字符串变量为全局时,该函数有效。但是由于我想避免不良做法,因此我试图将其传递给函数。我提供的MWE可以显示所有情况。

输入:

( "main",
         ["fred", 
                 ["barney"] ],
         ["george", 
                   ["jane", 
                           ["elroy"] ] ],
         ["homer", 
                  ["marge", 
                           ["bart"] ] ]
);

如果可以这样称呼,它基本上代表了一个树结构。它存储几个文件夹的层次结构。

为我纠正输出-

name my_gen_XML;


    name "main";
        name "fred";
            name "barney";
            name
        name
        name "george";
            name "jane";
                name "elroy";
                name
            name
        name
        name "homer";
            name "marge";
                name "bart";
                name
            name
        name

错误的输出-

name my_gen_XML;


    name "main";
        name
        name
        name

在下面的代码片段中,我提到了2种情况,分别称为工作状态和非工作状态。工作案例将产生正确的输出。不工作的情况提供了错误的输出。

我的部分代码-

    #!/usr/bin/perl
    use strict;
    use warnings;
    use Data::Dumper;        
    my @test = ( "main",
                        ["fred", 
                                ["barney"] ],
                        ["george", 
                                ["jane", 
                                        ["elroy"] ] ],
                        ["homer", 
                                ["marge", 
                                        ["bart"] ] ]
                );
    my $testRef = \@test;
    ## Working - Case
    #my $strBuffer;

    my $final = constructTree($testRef,"    name");

    print "$final \n";

    # Name : constructTree
    sub constructTree {
        my ($test, $indentStr) = @_;
        my $strBuffer = "";
        ## Non-Working Case
        $strBuffer = populateTree($test, $indentStr, $strBuffer);
        ## Working - Case
        #$strBuffer = populateTree($test, $indentStr);

        $strBuffer = "name my_gen_XML;\n\n\n".$strBuffer;

        return $strBuffer;
    }

    # Name : populateTree
    sub populateTree {
        ## Non-Working Case
        my ($array, $indentText, $strBuffer) = @_;
        ## Working - Case
        #my ($array, $indentText) = @_;
        my @list = @$array;

        $strBuffer .= "    $indentText \"$list[0]\";\n";
        $indentText = "    $indentText";
        shift(@list);

        foreach my $child ( @list ) {
            ## Non-Working Case
            populateTree(\@$child, $indentText, $strBuffer);
            ## Working - Case
            #populateTree(\@$child, $indentText);
            $strBuffer .= "    $indentText\n";
        }
        return $strBuffer;
    }

我尝试为$strBuffer使用状态变量,但无济于事。我也尝试捕获递归函数的输出,但这与我的情况重复。还尝试使用临时变量,但也无济于事。

我想知道如何使用“非工作代码”获得“正确的输出”。我以为这是一个简单的修复程序,但不确定。

2 个答案:

答案 0 :(得分:1)

以下是一个传递$strBuffer作为参考的示例:

sub constructTree {
    my ($test, $indentStr) = @_;

    my $strBuffer = "";
    my $strBufferRef = populateTree($test, $indentStr, \$strBuffer);
    $strBuffer = "name my_gen_XML;\n\n\n".$$strBufferRef;
    return $strBuffer;
}

sub populateTree {
    my ($array, $indentText, $strBuffer) = @_;

    my $item = shift @$array;
    $$strBuffer .= "    $indentText \"$item\";\n";
    my $newIndentText = "    $indentText";

    foreach my $child ( @$array ) {
        populateTree($child, $newIndentText, $strBuffer);
        $$strBuffer .= "    $indentText\n";
    }
    return $strBuffer;
}

输出

name my_gen_XML;


        name "main";
            name "fred";
                name "barney";
            name
        name
            name "george";
                name "jane";
                    name "elroy";
                name
            name
        name
            name "homer";
                name "marge";
                    name "bart";
                name
            name
        name

答案 1 :(得分:1)

语句my ($foo, $bar) = @_;将@_的每个元素复制到这些变量。这意味着,如果这些元素之一是字符串,则将其值复制到该变量,并且更改该变量不会更改原始变量。

除了将标量引用传递给字符串而不是字符串本身(如另一个答案(我建议)所述)之外,您还可以使用Perl的别名行为,但这通常不是众所周知的行为,因此对读者来说可能不太清楚(评论可能对您有用)。

@_本身的元素不是副本,而是别名,因此您可以通过直接访问该元素而不是将其复制到另一个变量来修改原始值,但这不是很容易理解。

sub populateTree {
    my ($array, $indentText) = @_;

    my $item = shift @$array;
    $_[2] .= "    $indentText \"$item\";\n";
    my $newIndentText = "    $indentText";

    foreach my $child ( @$array ) {
        populateTree($child, $newIndentText, $_[2]);
        $_[2] .= "    $indentText\n";
    }
    return $_[2];
}

在最新版本的Perl(5.22+)上,有一个名为refaliasing的实验性功能,可让您轻松创建自己的别名变量。

use experimental 'refaliasing';

sub populateTree {
    my ($array, $indentText) = @_;
    \my $strBuffer = \$_[2];

    my $item = shift @$array;
    $strBuffer .= "    $indentText \"$item\";\n";
    my $newIndentText = "    $indentText";

    foreach my $child ( @$array ) {
        populateTree($child, $newIndentText, $strBuffer);
        $strBuffer .= "    $indentText\n";
    }
    return $strBuffer;
}

CPAN模块Data::Alias允许您在任何版本的Perl上执行此操作。

use Data::Alias;

sub populateTree {
    my ($array, $indentText) = @_;
    alias my $strBuffer = $_[2];

    my $item = shift @$array;
    $strBuffer .= "    $indentText \"$item\";\n";
    my $newIndentText = "    $indentText";

    foreach my $child ( @$array ) {
        populateTree($child, $newIndentText, $strBuffer);
        $strBuffer .= "    $indentText\n";
    }
    return $strBuffer;
}

最后,您可以(ab)使用foreach loop的别名行为以一种稍微奇怪(但更兼容)的方式创建自己的别名。

sub populateTree {
    my ($array, $indentText) = @_;
    foreach my $strBuffer ($_[2]) {
        my $item = shift @$array;
        $strBuffer .= "    $indentText \"$item\";\n";
        my $newIndentText = "    $indentText";

        foreach my $child ( @$array ) {
            populateTree($child, $newIndentText, $strBuffer);
            $strBuffer .= "    $indentText\n";
        }
        return $strBuffer;
    }
}