Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shared variables in the context of subroutines vs anonymous subroutines

Tags:

perl

I saw this bit of code in an answer to another post: Why would I use Perl anonymous subroutines instead of a named one?, but couldn't figure out exactly what as going on, so I wanted to run it myself.

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!

In the above example, I received a warning: "Variable $a will not stay shared at line 15. Obviously, this is why the output is "unexpected," but I still don't really understand what's happening here.

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

In the same vein, I don't understand what's happening in this example either. Could someone please explain?

Thanks in advance.

like image 872
Steve P. Avatar asked May 07 '13 17:05

Steve P.


2 Answers

It has to do with compile-time vs. run-time parsing of subroutines. As the diagnostics message says,

When the inner subroutine is called, it will see the value of the outer subroutine's variable as it was before and during the first call to the outer subroutine; in this case, after the first call to the outer subroutine is complete, the inner and outer subroutines will no longer share a common value for the variable. In other words, the variable will no longer be shared.

Annotating your code:

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's memory will not be freed when the subroutine ends.

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

Typical output:

inner1: 123     SCALAR(0x80071f50)
inner2: 123     SCALAR(0x80071f50)
inner1: 456     SCALAR(0x80071f50)
inner2: 123     SCALAR(0x8002bcc8)
like image 151
mob Avatar answered Nov 26 '22 16:11

mob


You can print \&inner; in the first example (after definition), and print $inner; in second.

What you see are hex code references which are equal in first example and differ in second. So, in the first example inner gets created only once, and it is always closure to $a lexical variable from the first call of the outer().

like image 33
mpapec Avatar answered Nov 26 '22 15:11

mpapec