Есть ли лучший способ передачи по ссылке в Perl?
-
20-09-2019 - |
Вопрос
Я делаю передачу по ссылке следующим образом:
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
потому что это уродливо.
Решение
Вы смотрели на Данные::Псевдоним?Это позволяет вам создавать псевдонимы с лексической областью действия с чистым синтаксисом.
Вы можете использовать его для создания семантики передачи по ссылке, подобной этой:
use strict;
use warnings;
use Data::Alias;
sub foo {
alias my ($arg) = @_;
$arg++;
}
my $count = 0;
foo($count);
print "$count\n";
Результатом является 1
, указывающий на то , что вызов к foo
изменил свой аргумент.
Другие советы
sub repl {
my $line = \$_[0]; # or: my $line = \shift
$$line = "new value";
}
Есть несколько способов сделать это.Явно передайте скалярную ссылку в $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 для выполнения этой работы.
Метод first pass by ref использует тот факт, что Perl передает аргументы всем функциям в качестве ссылок.Элементы @_
фактически являются псевдонимами значений в списке аргументов при вызове подпрограммы.Путем изменения $_[0]
в foo()
, вы фактически изменяете первый аргумент на foo()
.
Второй метод pass by ref использует тот факт, что sub, вызываемый с &
символ и никакие скобки не получают @_
массив его вызывающего абонента.В остальном это идентично.
Обновить: Я только что заметил твое желание избежать $_[0]
.Вы можете сделать это в repl, если хотите:
sub repl {
for my $line( $_[0] ) {
$line = 'new value';
}
}
Я не думаю, что есть что-то неправильное в использовании local
чтобы создать псевдоним в этом случае.
Динамическая область видимости, конечно, является мощной функцией, но до тех пор, пока вы знаете о побочных эффектах (новое значение отображается в функциях, вызываемых из его области видимости, если лексика с тем же именем находится в области видимости, она не может быть локализована, ...), то это полезное дополнение к уже переполненному набору инструментов Perl.
Основная причина предупреждений в Perl docs о local
должны удерживать людей от непреднамеренного использования его вместо my
и облегчить переход с perl4.Но определенно бывают моменты, когда local
полезен, и это один из них.
Используя for
создать свой псевдоним тоже можно, но я нахожу явный синтаксис с local
яснее в своем намерении.Это также немного быстрее, если речь идет о производительности.