in reply to Perl scoping not logical...?
Lexicals are cleared at subroutine return, and allocated anew at the next invocation. It seems that there's also a transition between compilation and execution. What is hidden in <readmore> tags might add to the bewilderment and incite further investigation.
use strict; use warnings; use Devel::Peek; $\=$/; sub foo { my $bar = @_ ? shift : 1; print "in foo(): "; Dump($bar); sub bar { my $foo = shift; print "in bar(): "; Dump($bar); return $foo * (++$bar); }; my $result = bar($bar); $result; } print "calling foo(1): 1 * 2 = " . foo(1); print "calling foo(): 1 * 2 = " . foo(); print "calling foo(): 1 * 2 = " . foo(); print "calling bar(5): 3 * 5 = " . bar(5); print "calling foo(3): 3 * 4 = " . foo(3); __END__ Variable "$bar" will not stay shared at bar.pl line 17. in foo(): SV = IV(0x88d0b1c) at 0x88b57a4 REFCNT = 2 FLAGS = (PADBUSY,PADMY,IOK,pIOK) IV = 1 in bar(): SV = IV(0x88d0b1c) at 0x88b57a4 REFCNT = 2 FLAGS = (PADBUSY,PADMY,IOK,pIOK) IV = 1 calling foo(1): 1 * 2 = 2 in foo(): SV = IV(0x88d0e0c) at 0x88b5720 REFCNT = 1 FLAGS = (PADBUSY,PADMY,IOK,pIOK) IV = 1 in bar(): SV = IV(0x88d0b1c) at 0x88b57a4 REFCNT = 1 FLAGS = (PADBUSY,PADMY,IOK,pIOK) IV = 2 calling foo(): 1 * 2 = 3 in foo(): SV = IV(0x88d0e0c) at 0x88b5720 REFCNT = 1 FLAGS = (PADBUSY,PADMY,IOK,pIOK) IV = 1 in bar(): SV = IV(0x88d0b1c) at 0x88b57a4 REFCNT = 1 FLAGS = (PADBUSY,PADMY,IOK,pIOK) IV = 3 calling foo(): 1 * 2 = 4 in bar(): SV = IV(0x88d0b1c) at 0x88b57a4 REFCNT = 1 FLAGS = (PADBUSY,PADMY,IOK,pIOK) IV = 4 calling bar(5): 3 * 5 = 25 in foo(): SV = IV(0x88d0e0c) at 0x88b5720 REFCNT = 1 FLAGS = (PADBUSY,PADMY,IOK,pIOK) IV = 3 in bar(): SV = IV(0x88d0b1c) at 0x88b57a4 REFCNT = 1 FLAGS = (PADBUSY,PADMY,IOK,pIOK) IV = 5 calling foo(3): 3 * 4 = 18
Note how the addresses reported as "at 0x..." after the line "in bar():" are the same throughout, while the location in foo() changes after the second invocation. This might be a bug or a glitch in the perl engine, but at least it is a documented one (through diagnostics and warnings). AFAICS it is a compile-time/run-time issue.
There are two ways to circumvent this. One is the 'static variable hack' which will still emit the warning:
use strict; use warnings; sub foo { my $bar if 0; # <--- HACK! $bar = @_ ? shift : 1; sub bar { my $foo = shift; return $foo * (++$bar); } my $result = bar($bar); $result; } warn "calling foo(1): 1 * 2 = " . foo(1) . "\n"; warn "calling foo(): 1 * 2 = " . foo() . "\n"; warn "calling foo(): 1 * 2 = " . foo() . "\n"; warn "calling bar(5): 3 * 5 = " . bar(5) . "\n"; warn "calling foo(3): 3 * 4 = " . foo(3) . "\n"; __END__ Variable "$bar" will not stay shared at bar.pl line 17. calling foo(1): 1 * 2 = 2 calling foo(): 1 * 2 = 2 calling foo(): 1 * 2 = 2 calling bar(5): 3 * 5 = 15 calling foo(3): 3 * 4 = 12
This hack prevents the my $bar variable to be cleared, since the optimizer weeds out that code because of the constant comparison if 0;, but the compiler is glad since it has seen a lexical declaration.
The other way is making up an anonymous subroutine and assigning that to a typeglob:
use strict; use warnings; sub foo { my $bar if 0; $bar = @_ ? shift : 1; unless (*bar{CODE}) { my $bar_sub = sub { my $foo = shift; return $foo * (++$bar); }; *bar = $bar_sub; } my $result = bar($bar); $result; } warn "calling foo(1): 1 * 2 = " . foo(1) . "\n"; warn "calling foo(): 1 * 2 = " . foo() . "\n"; warn "calling foo(): 1 * 2 = " . foo() . "\n"; warn "calling bar(5): 3 * 5 = " . bar(5) . "\n"; warn "calling foo(3): 3 * 4 = " . foo(3) . "\n"; __END__ calling foo(1): 1 * 2 = 2 calling foo(): 1 * 2 = 2 calling foo(): 1 * 2 = 2 calling bar(5): 3 * 5 = 15 calling foo(3): 3 * 4 = 12
Nested subs is a dragon area (look for Abigail there)... ;-)
--shmem
_($_=" "x(1<<5)."?\n".q·/)Oo. G°\ /
/\_¯/(q /
---------------------------- \__(m.====·.(_("always off the crowd"))."·
");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
|
|---|