in reply to Constant value in anonymous sub

This is a perfectly good example of a closure -- by the book, one might almost say.

(Indeed, you want to take a good hard look at perldoc perlref. But if you're new to this, it might take a bit of study before the light comes on; so it did for me.)

Take another look at your own example code (a well-written example, thank you). You notice that when you execute &{$numbers{2}}; that $i is not really anywhere to be seen. Indeed, $i is out of scope. But by that time, you have defined three anonymous subroutines and put coderefs to them into the array @numbers. This is all fine and exactly what you want. You aren't keying %numbers with $i. You're keying with a literal, 2.

Let's move up to a more aggressive closure demo:

#!/usr/bin/perl use strict; use warnings; sub make_stepper { my $step = shift; my $i ; my $stepper = sub { my $usr_int = shift; my $j ; $i++; $j++; $usr_int *= $step; print '$i: ' , $i , "\t", '$j: ' , $j , "\t", '$usr_int: ' , $usr_int , "\t", "\n" ; }; return $stepper; }; print "Double: \n"; my $double = make_stepper( 2 ); &$double( 13 ); &$double( 22 ); print "\n"; print "Triple: \n"; my $triple = make_stepper( 3 ); &$triple( 13 ); print "\n"; print "Quadle: \n"; my $quadle = make_stepper( 4, 99 ); &$quadle( 13 ); &$quadle( 22 ); &$quadle( ); print "\n"; print "Reprise: \n"; &$double( 5 ); &$triple( 5 ); &$quadle( 5 ); __DATA__ Output: Double: $i: 1 $j: 1 $usr_int: 26 $i: 2 $j: 1 $usr_int: 44 Triple: $i: 1 $j: 1 $usr_int: 39 Quadle: $i: 1 $j: 1 $usr_int: 52 $i: 2 $j: 1 $usr_int: 88 Use of uninitialized value $usr_int in multiplication (*) at ./closure +-demo.pl line 18. $i: 3 $j: 1 $usr_int: 0 Reprise: $i: 3 $j: 1 $usr_int: 10 $i: 2 $j: 1 $usr_int: 15 $i: 4 $j: 1 $usr_int: 20 __END__

Here, $step and $stepper are enclosed in make_stepper(). Both are within the same lexical scope, so $stepper has no trouble at all making use of $step, which is shifted in whenever make_stepper() is called. Now $step goes out of scope as soon as its block terminates; but you might say it is "frozen" into the anonymous subroutine make_stepper() spits out.

Note that the second parm (99) is ignored when $quaddle is defined. This works as expected for 13 and 22; when $quaddle is called with no parameters, a warning is raised (and at the correct line of the code, too). This is what you want! The definition of $stepper is just that: a definition. It doesn't execute until instanced and called.

Take a look at the outputs of $i and $j. One might naively think both would be incremented.

But $j is not incremented from call to call; it goes out of scope at the end of the $stepper sub declaration and is never seen again. Each run of any of the instances creates a fresh $j, increments it to 1, prints it, and promptly forgets it.

Much more interesting is $i. This doesn't keep track of how many steppers have been made; rather it tells how often each one has been called. Note that each instance has its own counter. Think about this and see if you can tell why. Can you modify this demo to add a counter that does track how many steppers have been instanced?

Besides perldoc perlref, you might also want to take a look at Perl 5 Wiki and Perl.com on closures.

Replies are listed 'Best First'.
Re^2: Constant value in anonymous sub (closures & aliases)
by LanX (Saint) on Feb 19, 2010 at 14:00 UTC
    Sorry I disagree that this is a good example - "by the book" - of a closure, because it's far to magic.

    Each closure has a new instance of $i which is mutable, looking at the code I would expect either always the same instance of $i or $i being an alias of an immutable literal... but it's neither of these...

    for my $i (1..3) { $numbers{$i} = sub { $i++;print $i }; }

    Cheers Rolf

    UPDATE:

    OK my problem derived from the difference between range operator and comma operator in producing lists:

    DB<37> for my $i (1,2,3) { $numbers{$i} = sub { $i++;print $i }; pri +nt \$i} SCALAR(0x9338210)SCALAR(0x92bdc90)SCALAR(0x9338190) DB<38> &{$numbers{1}} Modification of a read-only value attempted at (eval 43)[/usr/share/pe +rl/5.10/perl5db.pl:638] line 2.
      Sorry I disagree that this is a good example - "by the book" - of a closure, because it's far to magic.
      Each closure has a new instance of $i which is mutable, looking at the code I would expect either always the same instance of $i or $i being an alias of an immutable literal... but it's neither of these...

      Rolf, $i is a variable and it is always mutable. At the end of the closure (the loop) it goes out of scope, so there is no way to reference it directly. There are still references to $i in the subroutines that were generated inside the loop/closure. Via these subroutines you can do whatever you like to $i, including returning it (as reference, of course) and manipulating it outside of a subroutine.

      Basically leaving a block only removes the symbol table entry for something. The object itself doesn't cease to exist until all references to it go away. This is very much by the book for Perl.

      - doug

        > Rolf, $i is a variable and it is always mutable.

        Doug, plz have a second look at the code (incl. errormsg) at the end of the post ... 8)

        Cheers Rolf

        UPDATE: Maybe it wasn't clearly enough expressed ...

        for my $i ( LIST ) produces _aliases_ to the listelements (even to read-only values)!

        It's NOT trivial (I would even say dangerous) when the explanation of closures has this extra complication, so it shouldn't be in any book.