#perl
#perl
Вопрос:
sub reduce(amp;@) {
my $expr = amp;{shift @ARG};
my $result = shift @ARG;
while (scalar @ARG > 0) {
our $a = $resu<
our $b = shift @ARG;
$result = $expr->();
}
return $resu<
}
Я не могу понять некоторую грамматику в этом коде. Кто-нибудь может мне объяснить? например amp;
, и $result = $expr->()
Комментарии:
1. Я не специалист по perl, но я уверен
$expr
, что это ссылка на подпрограмму . Я полагаюamp;{shift @ARG}
, что он преобразует первый аргумент в ссылку на подпрограмму и$expr->()
вызовет эту подпрограмму. Обратите внимание, что он делает значения$a
и$b
доступными для подпрограммы как глобальные.
Ответ №1:
amp;name
возвращает ссылку на подпрограмму с именем name
.
$code_ref->()
вызывает подпрограмму, на которую ссылается $code_ref
.
$ perl -e'
sub f { CORE::say "Hi" }
my $code_ref = amp;f;
$code_ref->();
'
Hi
В вашем случае shift @ARG
возвращает ссылку на подпрограмму. amp;{ $code_ref }
просто возвращает ссылку на код. Как таковой,
my $expr = amp;{shift @ARG};
можно было бы записать как
my $expr = shift @ARG;
Обратите внимание, что reduce
прототип позволяет вызывать его как
reduce { ... } ...
но на самом деле выполняется
reduce( sub { ... }, ... )
Обратите внимание, что эта версия reduce
содержит ошибки. Вы должны использовать тот, который предоставлен List::Util .
local $a
иlocal $b
должен был использоваться, чтобы избежать искажения значений, которые его вызывающий может иметь в$a
и$b
.- Эта версия
reduce
ожидает, что ее обратный вызов будет скомпилирован в том же пакете,reduce
что и она сама. В противном случае вспомогательный модуль обратного вызова не сможет просто использовать$a
and$b
. - Объявление переменных using
our
на самом деле совершенно бесполезно в этой версии, поскольку$a
и$b
освобождаются отuse strict;
проверок, а необъявленное использование$a
и$b
будет обращаться к тем же переменным пакета.
Комментарии:
1. как насчет наших переменных before ?
2. Они создают псевдонимы с лексической областью для переменных пакета
$a
и$b
. Это означает, что если текущий пакет естьFoo
, он создает псевдоним, вызываемый$a
для переменной$Foo::a
. Изменение одного приведет к изменению другого. К этим переменным будет обращаться указанная подпрограмма. Еслиmy $a
my $b
бы использовались и, они создавали бы переменные, которые не могла видеть указанная подпрограмма.
Ответ №2:
Возможно, поможет просмотр некоторых примеров List::Util::reduce() .
Давайте рассмотрим первый:
$foo = reduce { $a > $b ? $a : $b } 1..10;
Поэтому reduce
принимает a BLOCK
, за которым следует СПИСОК, который объявляется сигнатурой функции: sub reduce(amp;@) {
. Блок в нашем случае — это оператор $a > $b ? $a : $b
, а список — это 1..10
. Из документации:
Уменьшает @list, вызывая «BLOCK» в скалярном контексте несколько раз, каждый раз устанавливая $ a и $ b . Первый вызов будет с $ a и $ b, установленными для первых двух элементов списка, последующие вызовы будут выполняться путем установки $ a на результат предыдущего вызова и $ b на следующий элемент в списке.
Возвращает результат последнего вызова «БЛОКА». Если @list пуст, то возвращается «undef». Если @list содержит только один элемент, то этот элемент возвращается и «БЛОК» не выполняется.
А теперь перейдем к аннотированной версии кода:
$foo = reduce { $a > $b ? $a : $b } 1..10; # $foo will be set to 10
sub reduce(amp;@) {
# reduce() takes a BLOCK followed by a LIST
my $expr = amp;{shift @ARG};
# $expr is now a subroutine reference, i.e.
# $expr = sub { $a > $b ? $a : $b };
# Start by setting $result to the first item in the list, 1
my $result = shift @ARG;
# While there are more items in the list...
while (scalar @ARG > 0) {
# Set $a to the current result
our $a = $resu<
# Set $b to the next item in the list
our $b = shift @ARG;
# Set $result to the result of $a > $b ? $a : $b
$result = $expr->();
}
# List has now been reduced by the operation $a > $b ? $a : $b
return $resu<
}