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

Общие переменные в контексте подпрограмм против анонимных подпрограмм

Я видел этот бит кода в ответе на другое сообщение: Почему я должен использовать анонимные подпрограммы Perl вместо имени?, но не мог понять точно, что происходит, поэтому я хотел запустить его сам.

sub outer
{
  my $a = 123;

  sub inner
  {
    print $a, "\n"; #line 15 (for your reference, all other comments are the OP's)
  }

  # At this point, $a is 123, so this call should always print 123, right?
  inner();

  $a = 456;
}

outer(); # prints 123
outer(); # prints 456! Surprise!

В приведенном выше примере я получил предупреждение: "Переменная $a не останется открытой в строке 15. Очевидно, поэтому результат "неожиданен", но я до сих пор не совсем понимаю, что происходит здесь.

sub outer2
{
  my $a = 123;

  my $inner = sub
  {
    print $a, "\n";
  };

  # At this point, $a is 123, and since the anonymous subrotine 
  # whose reference is stored in $inner closes over $a in the 
  # "expected" way...
  $inner->();

  $a = 456;
}

# ...we see the "expected" results
outer2(); # prints 123
outer2(); # prints 123

В том же духе я не понимаю, что происходит в этом примере. Может кто-нибудь объяснит?

Спасибо заранее.

4b9b3361

Ответ 1

Это связано с анализом подпрограмм времени компиляции и времени выполнения. Как говорится в сообщении diagnostics,

Когда вызывается внутренняя подпрограмма, она увидит значение     переменная внешней подпрограммы, как это было до и во время первого     вызов внешней подпрограммы; в этом случае после первого вызова     внешняя подпрограмма завершена, внутренняя и внешняя подпрограммы не будут     более длинная доля общего значения для переменной. Другими словами,     переменная больше не будет использоваться.

Аннотирование кода:

sub outer
{
  # 'my' will reallocate memory for the scalar variable $a
  # every time the 'outer' function is called. That is, the address of
  # '$a' will be different in the second call to 'outer' than the first call.

  my $a = 123;


  # the construction 'sub NAME BLOCK' defines a subroutine once,
  # at compile-time.

  sub inner1
  {

    # since this subroutine is only getting compiled once, the '$a' below
    # refers to the '$a' that is allocated the first time 'outer' is called

    print "inner1: ",$a, "\t", \$a, "\n"; 
  }

  # the construction  sub BLOCK  defines an anonymous subroutine, at run time
  # '$inner2' is redefined in every call to 'outer'

  my $inner2 = sub {

    # this '$a' now refers to '$a' from the current call to outer

    print "inner2: ", $a, "\t", \$a, "\n";
  };

  # At this point, $a is 123, so this call should always print 123, right?
  inner1();
  $inner2->();

  # if this is the first call to 'outer', the definition of 'inner1' still
  # holds a reference to this instance of the variable '$a', and this
  # variable memory will not be freed when the subroutine ends.

  $a = 456;
}
outer();
outer();

Типичный выход:

inner1: 123     SCALAR(0x80071f50)
inner2: 123     SCALAR(0x80071f50)
inner1: 456     SCALAR(0x80071f50)
inner2: 123     SCALAR(0x8002bcc8)

Ответ 2

Вы можете распечатать \& inner; в первом примере (после определения) и напечатать $inner; во втором.

В первом примере вы видите шестнадцатеричные ссылки, которые равны и отличаются вторым. Итак, в первом примере внутренний создается только один раз, и он всегда замыкает $лексическую переменную от первого вызова внешнего().