Существует ли лучший способ передать ссылкой в Perl?

Я делаю передачу ссылкой как это:

use strict;
use warnings;

sub repl {
    local *line = \$_[0]; our $line;
    $line = "new value";
}

sub doRepl {
    my ($replFunc) = @_;
    my $foo = "old value";
    $replFunc->($foo);
    print $foo; # prints "new value";
}

doRepl(\&repl);

Существует ли более чистый способ сделать его?

Прототипы не работают, потому что я использую ссылку на функцию (доверяйте мне, что существует серьезное основание для использования ссылки на функцию).

Я также не хочу использовать $_[0] везде в repl потому что это ужасно.

6
задан brian d foy 18 March 2010 в 15:15
поделиться

4 ответа

Вы смотрели на Data :: Alias ​​? Он позволяет создавать псевдонимы с лексической областью видимости с чистым синтаксисом.

Вы можете использовать его для создания семантики передачи по ссылке следующим образом:

use strict;
use warnings;

use Data::Alias;

sub foo {
    alias my ($arg) = @_;
    $arg++;
}

my $count = 0;

foo($count);

print "$count\n";

Результатом будет 1 , что указывает на то, что вызов foo изменил его аргумент.

9
ответ дан 8 December 2019 в 16:01
поделиться

Есть несколько способов сделать это. Явно передайте скалярную ссылку в $ foo или воспользуйтесь встроенной в Perl семантикой передачи по ссылке.

Явная ссылка:

my $foo = "old value";
doRepl( \&repl, \$foo );
print $foo; # prints "new value";

sub repl {
    my $line = shift;
    $$line = "new value";
}

sub doRepl {
    my ($replFunc, $foo) = @_;
    $replFunc->($foo);
}

Передача по ссылке:

my $foo = "old value";
doRepl( \&repl, $foo );
print $foo; # prints "new value";

sub repl {
    $_[0] = "new value";
}

sub doRepl {
    my $replFunc = shift;
    $replFunc->(@_);
}

Еще более интересная передача по ссылке:

my $foo = "old value";
doRepl( \&repl, $foo );
print $foo; # prints "new value";

sub repl {
    $_[0] = "new value";
}

sub doRepl {
    my $replFunc = shift;
    &$replFunc;
}

Первый использует обычные жесткие ссылки Perl для выполнения работы.

Первый проход методом ref использует тот факт, что Perl передает аргументы всем функциям как ссылки. Элементы @_ фактически являются псевдонимами значений в списке аргументов при вызове подпрограммы. Изменяя $ _ [0] в foo () , вы фактически изменяете первый аргумент foo () .

Второй метод прохода по ссылке использует тот факт, что подпрограмма, вызываемая с сигилом & и без скобок, получает массив @_ вызывающей стороны. В остальном он идентичен.

Обновление: Я только что заметил, что вы хотите избегать $ _ [0] . Вы можете сделать это в repl, если хотите:

sub repl {
    for my $line( $_[0] ) {
        $line = 'new value';
    }
}
3
ответ дан 8 December 2019 в 16:01
поделиться

Я не думаю, что есть что-то плохое в использовании local для создания псевдонима в этом случае.

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

Основная причина предупреждений в документации Perl о local заключается в том, чтобы удержать людей от случайного использования его вместо my и облегчить переход от perl4. Но есть моменты, когда local полезен, и это один из них.

Использование for для создания псевдонима - тоже вариант, но я нахожу явный синтаксис с local более четким по смыслу. Это также немного быстрее, если вас беспокоит производительность.

0
ответ дан 8 December 2019 в 16:01
поделиться

sub repl {
    my $line = \$_[0];     # or: my $line = \shift
    $$line = "new value";
}
3
ответ дан 8 December 2019 в 16:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: