Я делаю передачу ссылкой как это:
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
потому что это ужасно.
Вы смотрели на 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
изменил его аргумент.
Есть несколько способов сделать это. Явно передайте скалярную ссылку в $ 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';
}
}
Я не думаю, что есть что-то плохое в использовании local
для создания псевдонима в этом случае.
Динамическая область видимости - это, конечно, мощная возможность, но пока вы знаете о побочных эффектах (новое значение видно в функциях, вызываемых из его области видимости, если одноименное лексическое значение находится в области видимости, его нельзя локализовать, ...), то это полезное дополнение к уже переполненному инструментарию Perl.
Основная причина предупреждений в документации Perl о local
заключается в том, чтобы удержать людей от случайного использования его вместо my
и облегчить переход от perl4. Но есть моменты, когда local
полезен, и это один из них.
Использование for
для создания псевдонима - тоже вариант, но я нахожу явный синтаксис с local
более четким по смыслу. Это также немного быстрее, если вас беспокоит производительность.
sub repl {
my $line = \$_[0]; # or: my $line = \shift
$$line = "new value";
}