Есть ли лучший способ перейти по ссылке в Perl? - PullRequest
6 голосов
/ 17 марта 2010

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

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, потому что это ужасно.

Ответы [ 4 ]

9 голосов
/ 17 марта 2010

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

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

use strict;
use warnings;

use Data::Alias;

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

my $count = 0;

foo($count);

print "$count\n";

Выходные данные 1, что указывает на то, что вызов foo изменил свой аргумент.

3 голосов
/ 17 марта 2010

sub repl {
    my $line = \$_[0];     # or: my $line = \shift
    $$line = "new value";
}
3 голосов
/ 17 марта 2010

Есть несколько способов сделать это. Явно передайте скалярную ссылку на $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().

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

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

sub repl {
    for my $line( $_[0] ) {
        $line = 'new value';
    }
}
0 голосов
/ 17 марта 2010

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

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

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

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

...