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

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

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

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?

4b9b3361

Ответ 1

Закрытие ограничивается только лексическими переменными; $_ обычно является глобальной переменной. В 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
  &{$_}();
}

Ответ 2

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

#!/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";
}