Perl: захват данных из рекурсивных вызовов - PullRequest
0 голосов
/ 16 октября 2018

Я написал функцию, которая принимает Array of Arrays и печатает его в формате 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 голосов
/ 16 октября 2018

Оператор 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;
    }
}
0 голосов
/ 16 октября 2018

Вот пример, передающий $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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...