Использование perl `my` в реальных аргументах функции - PullRequest
2 голосов
/ 27 февраля 2020

Я хочу использовать perl, чтобы построить график документа как можно более наглядным. Для повторного использования узлов я хочу ссылаться на узлы, используя переменные (или константы, если это проще). Следующий код работает и иллюстрирует идею с типами узлов, представленными литералами или вызовами фабричных функций для a и b. (Для простых демонстрационных целей функции не создают узлы, а просто возвращают строку.)

sub a (@) {
    return sprintf "a(%s)", join( ' ', @_ );
}   

sub b (@) {
    return sprintf "b(%s)", join( ' ', @_ );
}   

printf "The document is: %s\n", a(
    "declare c=",
    $c = 1, 
    $e = b( 
        "use",      
        $c,         
        "to declare d=",
        $d = $c + 1 
    ),      
    "use the result",
    $d,     
    "and document the procedure",
    $e      
);

Фактический и ожидаемый результат - The document is: a(declare c= 1 b(use 1 to declare d= 2) use the result 2 and document the procedure b(use 1 to declare d= 2)).

Моя проблема возникает из-за Я хочу use strict во всей программе, чтобы такие переменные, как $c, $d, $e, были объявлены с использованием my. Я, конечно, могу написать где-то близко к началу текста my ( $c, $d, $e );. Было бы более эффективно во время редактирования, когда я мог бы использовать ключевое слово my непосредственно при первом упоминании переменной, например:

…
printf "The document is: %s\n", a(
    "declare c=",
    my $c = 1,
    my $e = b(
        "use",      
        $c,         
        "to declare d=",
        my $d = $c + 1
    ),      
    "use the result",
    $d,     
    "and document the procedure",
    $e      
);

Это был бы мой любимый синтаксис. К сожалению, этот код выдает несколько Global symbol "…" requires explicit package name ошибок. (Более того, согласно документации, my ничего не возвращает.)

У меня есть идея такого использования my от использования, как в open my $file, '<', 'filename.txt' or die; или в for ( my $i = 0; $i < 100; ++$i ) {…}, где объявление и определение go в одном.

Поскольку узлы на графике являются константами, допустимо использовать что-то еще, кроме лексических переменных. (Но я думаю, что встроенные механизмы perl являются самыми сильными и наиболее эффективными для лексических переменных, поэтому я склонен в этом направлении.)

Моя текущая идея решить эту проблему - определить функция с именем что-то вроде define, которая за кадром будет манипулировать текущим набором лексических переменных, используя PadWalker или подобное. Однако это не позволило бы мне использовать естественный perl подобный синтаксис, такой как $c = 1, который был бы моим предпочтительным синтаксисом.

Ответы [ 3 ]

4 голосов
/ 27 февраля 2020

Я не уверен в точной необходимости, но вот один простой способ для подобных манипуляций.

В примере в OP требуется именованная переменная внутри самого оператора вызова функции, чтобы ее можно было использовать позже в это заявление для другого вызова et c. Если вам нужно это сделать таким образом, то вы можете использовать блок do для составления списка аргументов

func1(  
    do { 
        my $x = 5;
        my $y = func2($x);  # etc 
        say "Return from the do block what is then passed as arguments..."; 
        $x, $y
    }
);

Это позволяет вам делать то, что указано в вашем примере.

Если вы также хотите, чтобы имена были доступны в подпрограмме, передайте ha sh (или хэш-адрес) с подходящим образом выбранными именами ключей для переменных и в подпрограмме с именами ключей.

В качестве альтернативы рассмотрите возможность обычного объявления переменных перед вызовом функции. В этом нет ничего плохого, хотя есть много хорошего. Можно добавить немного обертки и сделать так, чтобы она выглядела красиво.


Более конкретно

printf "The document is: %s\n", a( do {
    my $c = 1;
    my $d = $c + 1;
    my $e = b( "use", $c, "to declare d=", $d );
    # Return a list from this `do`, which is then passed as arguments to a()
    "declare c=", $c, $e, "use the result", $d,"and document the procedure", $e 
} );

(сокращено для размещения здесь)

Этот блок do является промежуточной мерой для перемещения этого кода в подпрограмму, так как я предполагаю, что есть причины хотеть этого встроенного. Однако, поскольку комментарии указывают на то, что реальность еще более сложна, я призываю вас написать вместо этого нормальную подпрограмму (в которую можно построить график, кстати).

2 голосов
/ 27 февраля 2020

в соответствии с документацией my ничего не возвращает

В документации об этом не говорится, и это не так.

Разве вы этого не сделали? my $x = 123;? Если это так, вы присваиваете результат my $x. my просто возвращает вновь созданную переменную как lvalue (присваиваемое значение), поэтому my $x просто возвращает $x.

К сожалению, этот код выдает несколько ошибок [строгих переменных].

Символы (переменные), созданные my, видны только начиная со следующего оператора.

В лучшем случае в худшем случае допускается следующее:

my $x = 123;

{
   my $x = $x;
   $x *= 2;
   say $x;   # 246
}

say $x;   # 123

Я хочу использовать perl для построения графика документа как можно более наглядно.

Так почему бы не сделать это? Прямо сейчас вы строите строку, а не график. Постройте граф объектов, которые преобразуются в строку после построения графа. Вы можете построить эти объекты с помощью дерева дополнительных вызовов (declare( c => [ use( c => ... ), ... ] )). Я бы привел лучший пример, но грамматика того, что вы генерируете, мне не понятна.

1 голос
/ 27 февраля 2020

Ваш список аргументов содержит две ссылки на $c, $d и $e. Если вы префикс первой ссылки перед my, , то к моменту, когда Perl приступит к разбору второй ссылки , она будет вне области видимости, до следующего оператора, поэтому вторая ссылка будет ссылаться на другую переменную (которая может нарушать strict vars).

Объявить my ($c,$d,$e) перед вызовом вашей функции. В этом нет ничего плохого или неуместного.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...