Когда следует использовать & для вызова подпрограммы Perl?
Я слышал, что люди не должны использовать &
для вызова Perl subs, i.e:
function($a,$b,...);
# opposed to
&function($a,$b,...);
Я знаю, что список аргументов становится необязательным, но в каких случаях целесообразно использовать &
и случаи, когда вы его абсолютно не используете?
Также как здесь происходит увеличение производительности, если вы опускаете &
?
Ответы
Ответ 1
IMO, единственный раз, когда есть причина использовать &
, это если вы получаете или вызываете coderef, например:
sub foo() {
print "hi\n";
}
my $x = \&foo;
&$x();
Основное время, которое вы можете использовать, которое вы абсолютно не должны в большинстве случаев, - это вызов подпрограммы, у которой есть прототип, который задает поведение по умолчанию, отличное от стандартного. Я имею в виду, что некоторые прототипы позволяют переинтерпретировать список аргументов, например, преобразование спецификаций @array
и %hash
в ссылки. Таким образом, sub будет ожидать, что эти переинтерпретации произойдут, и если вы не зайдете на любую длину, необходимую для имитации их вручную, sub получит входные данные, сильно отличающиеся от ожидаемых.
Я думаю, что в основном люди пытаются сказать вам, что вы все еще пишете в стиле Perl 4, и теперь у нас есть намного более чистая и приятная вещь под названием Perl 5.
Что касается производительности, существуют различные способы, которыми Perl оптимизирует подзаголовки, которые &
проигрывает, причем одним из основных является вложение констант.
Существует также одно обстоятельство, когда использование &
обеспечивает преимущество в производительности: если вы переадресовываете подзаголовок с помощью foo(@_)
. Использование &foo
бесконечно быстрее, чем foo(@_)
. Я бы не рекомендовал его, если вы окончательно не обнаружили путем профилирования, что вам нужна эта микро-оптимизация.
Ответ 2
Я часто злоупотребляю &
, но в основном потому, что я делаю странные вещи интерфейса. Если вам не нужна одна из этих ситуаций, не используйте &
. Большинство из них - это просто доступ к определению подпрограммы, а не вызов подпрограммы. Все это в perlsub.
-
Взятие ссылки на именованную подпрограмму. Вероятно, это единственная распространенная ситуация для большинства Perlers:
my $sub = \&foo;
-
Аналогично, присваивая typeglob, который позволяет вам вызвать подпрограмму с другим именем:
*bar = \&foo;
-
Проверка того, что подпрограмма определена, как вы можете в тестовых наборах:
if( defined &foo ) { ... }
-
Удаление определения подпрограммы, которое не должно быть общим:
undef &foo;
-
Предоставление подпрограммы диспетчера, единственной задачей которой является выбор правильной подпрограммы для вызова. Это единственная ситуация, в которой я использую &
для вызова подпрограммы, и когда я ожидаю много раз вызывать диспетчера и выжимать небольшое количество операций из операции:
sub figure_it_out_for_me {
# all of these re-use the current @_
if( ...some condition... ) { &foo }
elsif( ...some other... ) { &bar }
else { &default }
}
-
Чтобы перейти в другую подпрограмму с использованием текущего стека аргументов (и заменить текущую подпрограмму в стеке вызовов), выполняется непрактичная операция при диспетчеризации, особенно в AUTOLOAD
:
goto ⊂
-
Вызвать подпрограмму, которую вы назвали после встроенного Perl. &
всегда дает вам пользовательский. Это почему мы учим его в Learning Perlя > . Вы действительно не хотите делать это нормально, но это одна из особенностей &
.
Есть несколько мест, где вы могли бы их использовать, но есть лучшие способы:
-
Чтобы вызвать подпрограмму с тем же именем, что и встроенный Perl. Просто не имеют подпрограмм с тем же именем, что и встроенный Perl. Проверьте perlfunc, чтобы просмотреть список встроенных имен, которые вы не должны использовать.
-
Чтобы отключить прототипы. Если вы не знаете, что это значит или почему вы хотите, не используйте &
. Некоторый код черной магии может понадобиться, но в таких случаях вы, вероятно, знаете, что делаете.
-
Чтобы разыменовать и выполнить ссылку подпрограммы. Просто используйте нотацию ->
.
Ответ 3
Форма & подпрограмма() отключает проверку прототипов. Это может быть или не быть тем, что вы хотите.
http://www.perl.com/doc/manual/html/pod/perlsub.html#Prototypes
Прототипы позволяют указывать числа и типы аргументов подпрограммы и проверять их во время компиляции. Это может обеспечить полезную диагностическую помощь.
Прототипы не применяются к вызовам методов или вызовам, выполненным в старомодном стиле с использованием префикса.
и необходимо указать или разыменовать подпрограмму или ссылку на код
например.
sub foo {
# a subroutine
}
my $subref = \&foo; # take a reference to the subroutine
&$subref(@args); # make a subroutine call using the reference.
my $anon_func = sub { ... }; # anonymous code reference
&$anon_func(); # called like this
Протипы также не применимы к ссылкам подпрограмм.
Форма подпрограммы также используется в так называемой форме magic goto.
Выражение goto &subroutine
заменяет текущий вызывающий контекст вызовом именованной подпрограммы с использованием текущего значения @_.
По сути, вы можете полностью переключить вызов на одну подпрограмму с вызовом названного. Это обычно наблюдается в блоках AUTOLOAD, где может быть сделан запрос отложенной подпрограммы, возможно, с некоторой модификацией к @_, но он полностью смотрит на программу, как если бы это был вызов именованного элемента.
например.
sub AUTOLOAD {
...
push @_, @extra_args; # add more arguments onto the parameter list
goto &subroutine ; # change call another subroutine, as if we were never here
}
}
Потенциально это может быть полезно для устранения хвостового вызова, я полагаю.
см. подробное объяснение этого метода здесь
Ответ 4
Я прочитал аргументы против использования '&', но я почти всегда использую его. Это экономит мне слишком много времени, чтобы не делать этого. Я трачу очень большую часть своего времени кодирования в Perl и ищу, какие части кода вызывают определенную функцию. С ведущим &, я могу найти и найти их мгновенно. Без ведущего &, я получаю инструкции определения функции, комментарии и отладки, обычно увеличивая количество кода, которое я должен проверить, чтобы найти то, что я ищу.
Главное не использовать '&' вы покупаете, вы можете использовать прототипы функций. Но прототипы функций Perl могут создавать ошибки так часто, как они их предотвращают, потому что они возьмут ваш список аргументов и переинтерпретируют его так, как вы не можете ожидать, так что вызов функции больше не пропускает аргументы, которые он буквально говорит, что это делает.