Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why "my @variable" inside a sub and modified by sub/sub behaves so strange

I have a strange behaved (to Python programmer) subroutine, which simplified as the following:

use strict;
use Data::Dumper;
sub a {
  my @x;
  sub b { push @x, 1; print "inside: ", Dumper(\@x); }
  &b;
  print "outside: ", Dumper(\@x);
}
&a;
&a;

I found the result is:

inside: $VAR1=[ 1 ]
outside: $VAR1 = [ 1 ]
inside: $VAR1=[1, 1]
outside: $VAR1= []

What I thought is when calling &a, @x is empty array after "my @x" and has one element after "&b", then dead. Every time I call &a, it is the same. so the output should be all $VAR1 = [ 1 ].

Then I read something like named sub routine are defined once in symbol table, then I do "my $b = sub { ... }; &$b;", it seems make sense to me.

How to explain?

like image 643
Forest Yang Avatar asked Dec 15 '22 02:12

Forest Yang


1 Answers

As per the "perlref" man page:

named subroutines are created at compile time so their lexical variables [i.e., their 'my' variables] get assigned to the parent lexicals from the first execution of the parent block. If a parent scope is entered a second time, its lexicals are created again, while the nested subs still reference the old ones.

In other words, a named subroutine (your b), has its @x bound to the parent subroutine's "first" @x, so when a is called the first time, b adds a 1 to @x, and both the inner and outer copies refer to this same version. However, the second time a is called, a new @x lexical is created, but b still points to the old one, so it adds a second 1 to that list and prints it (inner), but when it comes time for a to print its version, it prints out the (empty) brand new lexical (outer).

Anonymous subroutines don't exhibit this problem, so when you write my $b = sub { ... }, the inner @x always refers to the "current" version of a's lexical @x.

like image 126
K. A. Buhr Avatar answered May 24 '23 09:05

K. A. Buhr