Re: Closures & aliases
by diotalevi (Canon) on Jun 05, 2004 at 23:29 UTC
|
Foreach Loops The "foreach" loop iterates over a normal list value and sets the variable VAR to be each element of the list in turn. If the variable is preceded with the keyword "my", then it is lexically scoped, and is therefore visible only within the loop. Otherwise, the variable is implicitly local to the loop and regains its former value upon exiting the loop. If the variable was previously declared with "my", it uses that variable instead of the global one, but it's still localized to the loop. This implicit localisation occurs only in a "foreach" loop. $foo was bound to test() originally but then you slapped a localization over the binding so it was a separate variable being written to.
perl -le 'my $foo;print \$foo;for $foo(1){print \$foo}'
SCALAR(0x8152348)
SCALAR(0x81441c8)
| [reply] [d/l] |
|
|
Okay. That explains the code, but now begs the question, why is it so?
I can think of two or three situations where I would like to use a pre-declared loop variable in a for loop and have it retain it's value afterwards.
What I cannot think of, is any benefit for it not doing so, nor any major/common caveat that it avoids.
Is a pre-declared, lexical loop variable localized (something that you cannot ordinarially do) for the loop body duration, for a "least surprise" or other DWIM benefit? Or is it simple a side-effect or caveat of the implementation?
Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail
| [reply] |
|
|
A for loop and a foreach loop are slightly different. Adding to the confusion is that for and foreach are synonyms for each other (no pun intended).
for my $y ( @a ) {...}
makes $y an alias for one element of @a each time through the loop. Writing
foreach my $y ( @a ) {...}
is identical, but 4 chars longer to type.
On the other hand,
foreach ( $x=0; $x < $#a; $x++ ) {my $y=$a[$x];...}
would behave similarly, except $y is not an alias for elements of @a, but a copy. And using
for ( $x=0; $x < $#a; $x++ ) {my $y=$a[$x];...}
would not change the behavior here either.
Which form the compiler chooses is based on the argument list to for/foreach, and not the name itself!
I hope that helps :|
Update: I wish I'd have said it more like hv did below. Maybe he'll volunteer to edit for me in the future ;)
A for loop and a foreach loop are slightly different.
I attempted (but failed) to say that the OP seemed to understand them as different entities. The only difference is what follows the keyword, and not the keyword itself.
-QM
--
Quantum Mechanics: The dreams stuff is made of
| [reply] [d/l] [select] |
|
|
|
|
I don't know why, I jsut know it is documented that way. I guess foreach could make a copy of the value for each element but I'd guess it'd be slow then.
| [reply] |
|
|
I don't see why that would explain the behaiviour, as
for localizes the variable lexically, just like
local. That's probably so that you can use
$_ implicitly even if it's the index variable of
a for loop, for example
print and print uc for hello
prints helloHELLO.
This reply, however, has an answer
to your question.
| [reply] [d/l] [select] |
Re: Closures & aliases
by dave_the_m (Monsignor) on Jun 06, 2004 at 01:48 UTC
|
It's due to the way closures are currently impelemented.
Internally, sub 'test' has its own lexical $foo which just
happens to be an alias to to the $foo in the main file (technically each CV has its own scrachpad, each with a pointer to the same SV).
Within the for loop, the main $foo is now aliased to something
else (technically the pointer for $foo in main's pad temporarily points elsewhere), so the two $foo's are now different.
I haven't been able to think of a way of fixing this that
isn't inefficient, otherwise I would probably have done it by now.
Dave. | [reply] |
|
|
| [reply] |
|
|
Thanks for explaining.
I thought that when perl localizes a variable
(either with local or for)
it just saves the old value of the variable somewhere
(as an unnamed variable
in the lexical evironment of the block
in which you localize),
and restores it on exit.
(But it does the saving and restoring with an unwind-protect
so that if an exception brings out perl from the block,
the value gets restored.)
I belive that implementation
would not cause the faulty(?) behaiviour discussed
here.
I am still far from understanding perl internals,
so that may be a bad solution (and slower).
As you cannot localize a my variable in perl (which
might have some relation to how local is implemented),
I cannot reproduce that "bug" with local
instead of for.
Mit-scheme has the non-standard function
(Update:) special form
fluid-let, which is the equivalent of
perl's local (let is that of my).
With this, you can write
(let* ((v 2) (f (lambda () v))) (fluid-let ((v 5)) (f)))
This evaluates to 5, thus showing that f gets the new localized
value of v. If you change fluid-let to let, you get 2 of
course.
Update: This is of course just localization, not
a for loop, which may be quite different.
Because of the reason mentioned above,
you can not translate this code to perl as is.
If you use a global variable:
$v= 2; sub f {print $v} for $v (5) {f;}
or
$v= 2; sub f {print $v} {local $v= 5; f;}
you get 5 as expected. It is possible
that foreach is of a different nature than local in perl.
| [reply] [d/l] [select] |
|
|
The nub of the problem is that most people expect that in the
following code,
my $x = 1;
sub foo { $x }
There is a single variable called $x which is accessible from the main program and from within foo. The way it happens to
be implemented internally is that they are treated as two
separate variables that happen to be initially aliased
to the same value. Here's a little ASCII diagram.
Before the for loop; the two X's point to the same value
$MAIN_X ---
\
-> +---+
$FOO_X ------> | 1 |
+---+
and during for $x (9) { ...
+---+
$MAIN_X -----> | 9 |
+---+
+---+
$FOO_X ------> | 1 |
+---+
So calling foo from within the loop causes the 1 to be printed, not the 9.
You are mostly right about how localization and for aliasing
work, expect that its not entire values that are temporarily
saved, it is simply a C pointer, either in the symbol table
(eg local $x), in the scratchpad (eg my $x; for $x ()), or
in an array or hash slot (eg local $h{foo}), that is pushed
onto perl's savestack and replaced with a pointer to a different value.
Dave. | [reply] [d/l] [select] |
|
|