Подтвердить что ты не робот

Как работают закрытия в 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");

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

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

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

4b9b3361

Ответ 1

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

sub { ... } оценивает код coderef, поэтому вы можете напрямую назначить его скалярной переменной. Если вы хотите назначить именованную функцию (например, 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 и, следовательно, новое замыкание. Каждый раз, когда он вызывается, возвращается другое закрытие.

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

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

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

Ответ 2

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

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

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

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

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

и, наконец, В этом коде выше, как в нем ** были слова мира и землянины добавлены к словам howdy и приветствиям.

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

Ответ 3

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

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