Как работают замыкания в Perl?

Новичок в Perl снова здесь, пытаясь понять closure в Perl.

вот пример кода, который я не понимаю:

sub make_saying  {
    my $salute = shift;
    my $newfunc = sub {
        my $target = shift;
        print "$salute, $target!n";
    };
    return $newfunc;            # Return a closure
}
$f = make_saying("Howdy");      # Create a closure
$g = make_saying("Greetings");  # Create another closure
# Time passes...
$f->("world");
$g->("earthlings");

Итак, мои вопросы:

  1. если переменная назначена функции, является ли она автоматически ссылкой на эту функцию?
  2. в указанном выше коде, я могу написать $f = make_saying("Howdy") вместо? И когда я могу использовать & потому что я попытался использовать это при передаче параметров (&$f("world")), но это не работает.
  3. и, наконец, в этом коде выше, как он* * сделал слова world и earthlings добавляются к словам howdy и greetings.

примечание: Я понимаю, что $f несколько привязан к функции с параметром howdy так вот как я понимаю, как world есть дописывается. Чего я не понимаю, так это 2-й функции внутри. Как он действует своей магией. Прости, я правда не знаю, как спросить об этом.

3 ответов


в Perl скалярные переменные не могут содержать подпрограммы напрямую, они могут содержать только ссылки. Это очень похоже на то, что скаляры не могут содержать массивы или хэши, только arrayrefs или hashrefs.

на sub { ... } вычисляет кодер, поэтому вы можете напрямую назначить его скалярной переменной. Если вы хотите назначить именованную функцию (например,foo), вы должны получить ссылку типа \&foo.

вы можете вызвать coderefs как $code->(@args) или &$code(@args).

в код

$f = \make_saying("Howdy")

оценивает make_saying("Howdy"), и принимает ссылку на возвращаемое значение. Так вы получите ссылку, которая указывает на coderef, не coderef.

поэтому его нельзя назвать как &$f("world"), вам нужно разыменовать один дополнительный уровень: &$$f("world").


замыкание-это функция, которая привязана к определенной среде.

среда состоит из всех видимых в данный момент переменных, поэтому закрытие всегда запоминает это масштаб. В коде

my $x;
sub foo {
  my $y;
  return sub { "$x, $y" };
}

foo является закрытием$x, как внешняя среда состоит из $x. Внутренняя саб закрытия $x и $y.

каждый раз foo выполняется, получаем новый $y и, следовательно, новое закрытие. Каждый раз, когда это называется, a разные закрытие возвращается.

когда мы выполняем make_saying("Howdy") на $salute переменная имеет значение Howdy. Возвращенное закрытие помнит это масштаб.

когда мы выполняем его снова с make_saying("Greetings") тело make_saying оценивается снова. The $salute теперь Greetings, и внутренний sub закрывается над этой переменной. Эта переменная отделена от предыдущей $salute, который все еще существует, но доступен только через первое закрытие.

два встречающих закрылись над отдельными $salute переменные. Когда они будут казнены, их соответствующие $salute по-прежнему в области, и они могут получить доступ и измените значение.


если переменная asigned к функции, то она автоматически a ссылка на эту функцию?

нет. В примере функция make_saying return ссылка на другую функцию. Такие замыкания не имеют имени и могут перехватывать переменную вне ее области (переменная $salute в вашем примере).

в этом выше коде я могу написать $f = \make_saying("Howdy") вместо этого? И когда я могу использовать&, потому что я пытался использовать это в прохождение параметры (&$f ("мир")), но он не работает.

нет. $f = \make_saying("Howdy") - это не то, что ты думаешь (читай Амон пост для деталей). Вы можете написать $f = \&make_saying; что означает "поместить в $f ссылку на функцию make_saying". Вы можете использовать его позже следующим образом:

my $f = \&make_saying;
my $other_f = $f->("Howdy");
$other_f->("world");

и, наконец, в этом коде выше, как в нем * * слова мир и земляне приложились к словам "привет и привет". приветствия.

make_saying создания мой переменная, которая входит в lamda (my $newfunc = sub); эта лямбда возвращается из make_saying. Он содержит данное слово "магазина" через "закрытие" (? извините, не знаю, какое слово на английском языке).


каждый раз, когда вы вызываете подпрограмму "make_saying", она: 1a-создать другое закрытие 2a-присвоить полученный параметр скалярному "$ salute" 3A-объявить (создать, но не выполнить) внутреннюю анонимную подпрограмму: именно по этой причине в данный момент ничто не назначается скалярной " $ target "и не выполняется оператор "print" $salute, $target!\северный';" 4a-наконец, подпрограмма "make_saying" возвращает ссылку на внутреннюю анонимную подпрограмму, эта ссылка становится единственной способ вызова (конкретной) анонимной подпрограммы.

когда-либо вы называете каждую анонимную подпрограмму, это: 1b-присвоить полученный параметр скалярной "$target" 2b-см. также скалярный "$ salute", который будет иметь значение, назначенное в момент создания анонимной подпрограммы (когда была вызвана ее родительская подпрограмма "make_saying" 3b-наконец, выполните инструкцию "print" $salute, $target!\n";'