some puzzles

Michael Fowler michael at shoebox.net
Tue Mar 20 16:33:50 CST 2001


On Tue, Mar 20, 2001 at 11:40:58AM -0800, Rick J wrote:
> Thanks for the explanation on scoping, and great site
> http://perl.plover.com. I have read most of articles. 
> I have a question about my scope as follow.

You're most welcome.

 
> use strict;
> my $count = 0;
> callsub() while ($count++ < 3);
> {
> 	my $num = 10;
> 	sub callsub {
> 		print ++$num, "\t";
>  	}
> 	print "$num\n";
> }
> It prints out 1 2 3 10.
>
> Is my understanding right? The first time when
> callsub() was called, the callsub was in the outer
> block where my $num was defined. So local $num was
> declared and defined, however, the process didn't
> reach the statement 'my $num = 10', so it did not get
> the defined value and printed as 1 2 3 respectively
> instead of 11 12 13. However, after the while loop was
> done, and this time, the program pass that statement,
> and got the value and printed it as 10.

You're dealing with two issues here.  One is a closure, the other is
confusion about what happens when a subroutine is defined.

You should probably find a more robust description of clusures, but briefly
a closure is a subroutine that carries a set of lexical variables (defined
outside of the subroutine definition, but in the same scope) around with it. 
The concept can be difficult to grasp; I will try to elaborate with your
code.

So the first thing you're seeing is the closure, callsub(), in action. 
callsub() is defined in the same scope as the lexical $num, and it uses $num
in the body (of the subroutine), therefore it is a closure around $num. 
$num has no value initially, because when you first call callsub() here:

    callsub() while ($count++ < 3);

the "$num = 10;" line has not yet been encountered.  callsub() increments
the lexical $num, which is effectively at 0, to 1.  This is why you see the
sequence 1, 2, 3.

After that code, you enter the block:

    {
        my $num = 10;
        sub callsub {
            print ++$num, "\t";
        }
        print "$num\n";
    }

$num is initialized to 10, and subsequently printed out.  callsub() may be
defined here, but it is not actually called, so no increment takes place.


> I am still wondering why in the sub, it did not print
> 11 12 13. My here, looks more like local, because in
> that block, it stores 2 values for the my $num, one is
> 10, and the other is 1, 2, or 3. I am so clear and
> sure.

If you want to see the sequence 11, 12, 13 you have to initialize $num to 10
before calling callsub().  In your code you haven't done that.

What can be confusing is when statements are encountered.  Recall I said the
initialization of $num wasn't encountered before the first call to
callsub(), but it's still a lexical variable.  The reason is the my operator
has a compile-time effect, whereas assignment is done at runtime.  So while
$num has been declared lexical, the code initializing it has not yet been
encountered by the time callsub() is first called.



> Another question is about assignment. I know that in
> assignment, if the right side is array, and left side
> is list or array, it just assigns the values
> accordingly. If the left side is scalar, it assigns
> the total number of elements in that array to the
> scalar variable. So the following cases indicate:

Here you seem to be confusing arrays and lists.  An array in scalar context
yields the number of elements in the array; a list in scalar context yields
the last element of the list.  The simple rule of thumb is that an array is
prefixed by the @ symbol, a list is not.


> 2. $var = (10, 20, 30); #$var is 30

(10, 20, 30) is a list.


> 3. my ($v1, $v2, @arr);
>    $v1 = (($v2, @arr) = (10, 20, 30, 40)); 
>    #$v1 is 4, $v2 is 10 and @arr has 20 30 40

This is a special case.  A list assignemnt (that's the "($vw, @arr) ="
you're using) in scalar context yields the number of elements on the right
side of the assignment, much like an array, but it's still a list.


> 4. @arr = (10, 20, 30);
>    $var = (@arr); #$var is 3  

What you're seeing is a list of one element being evaluated in scalar
context, yielding the last element, @arr.  @arr is then evaluated in scalar
context, yielding 3.


> 5. $var = 10, 20, 30; #$var is 10

This is simply a list of statements, where "$var = 10", "20", and "30" are
the individual statements.  You would use such a thing in something like:

    print("test passed"), $test_failed = 0, next  if $test_passed;



Michael
--
Administrator                      www.shoebox.net
Programmer, System Administrator   www.gallanttech.com
--

=================================================
Mailing list info:  If at any time you wish to (un|re)subscribe to
the list send the request to majordomo at hfb.pm.org.  All requests
should be in the body, and look like such
                  subscribe anchorage-pm-list
                  unsubscribe anchorage-pm-list



More information about the Anchorage-pm mailing list