закрытие Perl и $ _ - PullRequest
       32

закрытие Perl и $ _

17 голосов
/ 12 января 2011

Одна из первых вещей, которые я пытаюсь выучить на незнакомом языке программирования, - как он обрабатывает замыкания. Их семантика часто переплетается с тем, как язык обрабатывает области видимости, и различными другими хитрыми моментами, поэтому их понимание раскрывает некоторые другие аспекты языка. Кроме того, укупорочные средства являются действительно мощной конструкцией и часто сокращают количество шаблонов, которые мне приходится набирать. Поэтому я возился с замыканиями в Perl и наткнулся на небольшую ошибку:

my @closures;
foreach (1..3) {
  # create some closures
  push @closures, sub { say "I will remember $_"; };
}
foreach (@closures) {
  # call the closures to see what they remember
  # the result is not obvious
  &{$_}();
}

Когда я написал приведенный выше код, я ожидал увидеть

I will remember 1
I will remember 2
I will remember 3

но вместо этого я получил I will remember CODE(0x986c1f0).

Вышеупомянутый эксперимент показал, что $_ очень зависит от контекста, и если он появляется в замыкании, то его значение не фиксируется в момент создания замыкания. Это ведет себя больше как ссылка. О каких еще ошибках я должен знать при создании замыканий в perl?

Ответы [ 2 ]

25 голосов
/ 12 января 2011

Закрытия закрываются только по лексическим переменным;$_ обычно является глобальной переменной.В 5.10 и выше вы можете сказать my $_;, чтобы оно было лексическим в заданной области (хотя в 5.18 это задним числом было объявлено экспериментальным и может быть изменено, поэтому лучше использовать другое имя переменной).

В результате вы получите ожидаемый результат:

use strict;
use warnings;
use 5.010;
my @closures;
foreach my $_ (1..3) {
  # create some closures
  push @closures, sub { say "I will remember $_"; };
}
foreach (@closures) {
  # call the closures to see what they remember
  # the result is not obvious
  &{$_}();
}
4 голосов
/ 28 июня 2011

$ _ является глобальной переменной и не должна использоваться в замыкании. Перед использованием присвойте его переменной с лексической областью, как показано ниже. Это даст ожидаемый O / P.

#!/usr/bin/perl -w

use strict;
my @closures;


foreach (1..3) {
   my $var = $_;
   push @closures, sub { print "I will remember $var"; };
}

foreach (@closures) {
  $_->();
  print "\n";
}
...