I'm really puzzled. Recently when reading through the perlref article, I came across a little example of closures. The example first confused me, and then made more sense, and then confused me again. Here is how.

At first, I was confused because, according to my thinking, each of the generated functions should print out the same value: <FONT COLOR='violet'>....</FONT>. After all, 'violet' is the last known value of $name.. Here is the example:

for my $name (@colors) { no strict 'refs'; # allow symbol table manipulation *$name = sub { "<FONT COLOR='$name'>@_</FONT>" }; }

Then it occured to me that I didn't look carefully enough: $name was local to the loop (it is declared for my $name ....) so maybe the value set by one loop iteration didn't affect the value in the next. Problem solved?

Well, no. To test this idea out I decided to experiment a bit by moving the declaration to outside of the for loop. Just for kicks I did this using both my and our to declare it. This resulted in some odd behavior that I'm having trouble understanding. When our is used to declare $name the closure sometimes complains of an undefined variable. When my is used to declare $name, perl sometimes refuses to change the value of the variable. Is this normal? is it a bug? I would be grateful to any fellow monks that can help me form a mental model to explain this behavior. I don't feel I understand this issue well enough to make a judgment either way.

I've put together an annotated script with some examples:

use strict; use warnings; # declaration of $name # pick one and comment out the other my $name; #our $name; # why is red printing out red rather than violet since # $name is always changing? Shouldn't it be picking up the # most recent value of $name, i.e. 'violet'? my @colors = qw(red blue green yellow orange purple violet); for $name (@colors) { no strict 'refs'; # allow symbol table manipulation *$name = sub { "<FONT COLOR='$name'>@_</FONT>" }; } # And another oddity: when we declare <c>our $name</c> *both* # red and violet complain about uniinitialized values even # though we have clearly set <c>$name</c> inside the for loop. # No such problems when we declare <c>$name</c> using <c>my</c>. print "----- \$name is constant inside sub ------\n"; print "red: ${\(red())}\n"; print "violet: ${\(violet())}\n"; # is it because our lack of use of $name within the generated # sub caused it to be compiled as a constant? # no: here we use $name, but it *still* prints out the value # at sub definition time, rather than the current value. for $name (@colors) { no strict 'refs'; # allow symbol table manipulation no warnings 'redefine'; #ignore noise about redefinitions *$name = sub { my $sOutput = "<FONT COLOR='$name'>@_</FONT>"; $name = 'something wacky and wonderful'; #change the value return $sOutput; }; } # when we declare <c>our $name</c> red, but not violet # complains about uninitialized values. There are no # complains when we declare <c>my $name</c>. Hmmm... print "----- \$name given new value inside sub ------\n"; print "red: ${\(red())}\n"; print "violet: ${\(violet())}\n"; # so maybe the closure picks up the value at the time of # definition and always uses that as the initial value even # if the value changes after subdefinition? # # but if so, why do red and violet use their own last value # setting of the value of name? Didn't we just redefine the # subroutines? shouldn't they be using the value at the time # of definition? print "----- sub used inside loop that defined it ------\n"; for $name (@colors) { no strict 'refs'; # allow symbol table manipulation no warnings 'redefine'; #ignore noise about redefinitions # why isn't $name getting set to the array eleemnt value # when the array element is red or violet? # Note: it gets set properly when we declare # <c>our $name</c> but not when we declare <c>my $name</c> print "trying out $name: "; *$name = sub { my $sOutput = "<FONT COLOR='$name'>@_</FONT>"; $name = 'something wacky and wonderful'; #change the value return $sOutput; }; print $name->() . "\n"; } print "----- sub used outside of loop that assigned it ------\n"; print "red: ${\(red())}\n"; print "green: ${\(green())}\n"; print "violet: ${\(violet())}\n";

Here is the output generated when $name is declared using my $name.

----- $name is constant inside sub ------ red: <FONT COLOR='red'></FONT> violet: <FONT COLOR='violet'></FONT> ----- $name given new value inside sub ------ red: <FONT COLOR='red'></FONT> violet: <FONT COLOR='violet'></FONT> ----- sub used inside loop that defined it ------ trying out something wacky and wonderful: <FONT COLOR='something wacky + and wonderful'></FONT> trying out blue: <FONT COLOR='blue'></FONT> trying out green: <FONT COLOR='green'></FONT> trying out yellow: <FONT COLOR='yellow'></FONT> trying out orange: <FONT COLOR='orange'></FONT> trying out purple: <FONT COLOR='purple'></FONT> trying out something wacky and wonderful: <FONT COLOR='something wacky + and wonderful'></FONT> ----- sub used outside of loop that assigned it ------ red: <FONT COLOR='something wacky and wonderful'></FONT> green: <FONT COLOR='something wacky and wonderful'></FONT> violet: <FONT COLOR='something wacky and wonderful'></FONT>

And here is the same script run when my $name is commented out and replaced by our $name.

----- $name is constant inside sub ------ Use of uninitialized value in concatenation (.) or string at ... red: <FONT COLOR=''></FONT> Use of uninitialized value in concatenation (.) or string at ... violet: <FONT COLOR=''></FONT> ----- $name given new value inside sub ------ Use of uninitialized value in concatenation (.) or string at ... red: <FONT COLOR=''></FONT> violet: <FONT COLOR='something wacky and wonderful'></FONT> ----- sub used inside loop that defined it ------ trying out red: <FONT COLOR='red'></FONT> trying out blue: <FONT COLOR='blue'></FONT> trying out green: <FONT COLOR='green'></FONT> trying out yellow: <FONT COLOR='yellow'></FONT> trying out orange: <FONT COLOR='orange'></FONT> trying out purple: <FONT COLOR='purple'></FONT> trying out violet: <FONT COLOR='violet'></FONT> ----- sub used outside of loop that assigned it ------ red: <FONT COLOR='something wacky and wonderful'></FONT> green: <FONT COLOR='something wacky and wonderful'></FONT> violet: <FONT COLOR='something wacky and wonderful'></FONT>

Many thanks in advance, beth


In reply to How do closures and variable scope (my,our,local) interact in perl? by ELISHEVA

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.