Re: Re: for loop localisation bug?
by cees (Curate) on Dec 29, 2003 at 16:30 UTC
|
It has to do with the special way that foreach works. In the statement foreach $n (1, 2, 3) {}, $n actually becomes the value in the LIST on each interation of the loop. It doesn't just get a copy of it in the variable $n. So in your example, $n would actually contain the read-only value 1 on the first iteration of the loop.
So what would happen if after the loop you tried to assign a value to $n? If it was not localized in the foreach loop, then you would be trying to assign a value to a read-only value, and you would get an error.
my $n;
foreach $n ( 1, 2, 3 ) { }
$n = 'foo';
If $n was not localized, you would get a "Modification of a read-only value attempted ..." error in your code.
A better explaination of this issue can be found here in the perl5-porters archive.
- Cees | [reply] [d/l] |
|
|
Just to add a term: the name for this kind of behaviour is aliasing. Which is a concept that people seem to have little trouble using in practice, but much harder to understand and explain. Some languages have no concept of aliasing at all. In perl @_ is aliased, as are the iterator in a foreach loop, grep, map and sort. In Pascal "var" parameters are essentially aliases, as are normal parameters in VB (byref parameters). Its my understanding that C has no aliases.
I find a useful way of thinking about aliases is that they are like references that need not be dereferenced.
---
demerphq
First they ignore you, then they laugh at you, then they fight you, then you win.
-- Gandhi
| [reply] [d/l] |
Re: Re: for loop localisation bug?
by calin (Deacon) on Dec 29, 2003 at 16:08 UTC
|
My guess: could it be this way because otherwise it would interfere with the aliasing properties of foreach?
$ perl
my ($i, $x, $y);
for $i ($x, $y) {
$i = 5;
}
print "=$x=$y=\n";
^D
=5=5=
| [reply] [d/l] [select] |
Re^2: for loop localisation bug?
by Aristotle (Chancellor) on Dec 29, 2003 at 17:44 UTC
|
I find calling something broken before you even know the reason to be.. daring to say the least.
If it were "fixed", a number of things we're used to doing with the aliasing property of foreach such as s/foo/bar/ for @array; would cease to work. Frankly I wouldn't have it another way - more than a few times have I found the persisting iteration variable value behaviour of FOR loops in Pascal and C annoying, and more than a few times have I found the aliasing behaviour of Perl's foreach useful.
If you need the value, then just do manually what Perl avoids to: copy it to a variable from the surrounding scope on each iteration.
Makeshifts last the longest.
| [reply] [d/l] |
|
|
Fair enough. My choice of the word broken, even when expressed only as 'my opinion', was probably rash.
I should have said, unintuative, unhelpful or (as I did) Doing What I Didn't Mean.
By using a predeclared variable as the loop counter, I am explicitly attempting to retain the exit value of loop counter. This behaviour is implicitly overriding that attempt -- in order to provide a behaviour that is available to me explicitly, by declaring the loop variable within the for loop construct for my $n ( ... ) -- and hence not doing what I mean.
I understand, utilise, and admire the design of the aliasing done by the for loop, and I agree with you that it is a useful feature that is DWIM in most instances. The fact that I couldn't explicitly override it when I tried to, took me by surprise, and (still) leaves me wishing that I could.
I guess that this is a throwover from pre-lexical days rather than an explicitly sought-after design feature. Then, someone will pop up and tell me that I guess wrong:)
In any case, I didn't mean to indicate that it should be fixed at this late stage, just that I would prefer that implicit behaviours would take a back seat to explicit behaviours. In most cases, perl gets that right (IMO), but in this instance, probably as a matter of history, I think it gets this wrong.
No biggy, just an embarrased reaction to an implicit behaviour (that I had never before encountered) overriding my explicit attempt to define WIM. Despite reducing the problem to it's simplest in a test script and spending half an hour staring at it to see what I was doing wrong, it turned out to be "user error".
I didn't even consider looking it up, because based upon both my intuition, and experience of other languages, the behaviour I was seeing "couldn't possibly be correct". The fact that it is documented as working this way means it is 'correct', but I would never have guessed that it would be so.
Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail
Hooray!
| [reply] [d/l] |
|
|
Don't worry you are not alone in being caught by this, see (s)coping with foreach :-)
-- Do not seek to follow in the footsteps of the wise. Seek what they sought. -Basho
| [reply] |
|
|
I am explicitly attempting to retain the exit value of loop counter. This behaviour is implicitly overriding that attempt -- in order to provide a behaviour that is available to me explicitly, by declaring the loop variable within the for loop construct for my $n ( ... )
[ ... ]
I would prefer that implicit behaviours would take a back seat to explicit behaviours. In most cases, perl gets that right (IMO), but in this instance, probably as a matter of history, I think it gets this wrong.
You're confusing implicit and explicit behaviours. foreach is an explicit aliasing construct and never behaves any differently.
This is a conscious design decision. It's not going to change come Perl 6 either. Aliasing is not reasonably feasible if you want to retain the value past the loop, unless you're willing to introduce convoluted semantics about when the value retained is a copy and when it is still an alias. Your proposed semantics would therefor have to be implemented as something along the lines of "aliasing when my, copying when absent". Of course that would require some extra special case treatment of $_.
I'd prefer simply having foreach be consistent with itself even if there wasn't a lot of legacy code to cater for. So I'll say that Perl got this one right.
Makeshifts last the longest.
| [reply] |
|
|
This is a real issue that bites people from time to time, and it seems to me that it could be fixed with a minor extension of syntax to specify that a foreach loop should not do aliasing. Of course, it can also be addressed by a little code (and awareness):
my $n; # Going to set this
for my $a (1..100) {
if ($a == 50) {
$n = $a;
last;
}
}
# $n is 50
The advantage to new syntax would be that it promotes awareness of the two non-compatible WIMs. It could also have the ever-so-slight advantage of reducing the number of variables declared. I've thought of three possible syntax extensions:
- An alias pragma, on by default:
no alias;
for $n (1..100)
- The addition of an equals sign to indicate that each element is assigned, rather than aliased:
for $n = (1..100)
- Using a ref to a scalar var instead of a scalar var to indicate non-localization:
for \$n (1..100)
None of these would affect existing code (unless there's already an alias module/pragma of which I'm unaware).
The PerlMonk tr/// Advocate
| [reply] [d/l] [select] |
|
|
Frankly I wouldn't have it another way - more than a few times have I found the persisting iteration variable value behaviour of FOR loops in Pascal and C annoying, and more than a few times have I found the aliasing behaviour of Perl's foreach useful.
Just to throw my two pennies in on this one. I feel that that's merely the, "feature set," of the different languages. There are different instances where I would rather have the iteration variable persist and other times where I don't want it to persist.
I guess what I'm saying is that not every language can meet the needs of every situation nor make every developer happy. I can't vouch for anyone else, but that's why I have so many different languages installed. Different tools for different needs.
----
Is it fair to stick a link to my site here?
Thanks for you patience.
| [reply] |
Re: Re: for loop localisation bug?
by Corion (Patriarch) on Dec 29, 2003 at 16:44 UTC
|
In both, C and Pascal, the value of the loop counter variable after leaving the loop (however) is undefined. This is to allow the compiler to optimize its loop construct, as some compilers only have one internal loop construct and convert the syntactic candy of for, while and their ilk to it - or at least I believe that's why it was defined as undefined/implementation specific.
perl -MHTTP::Daemon -MHTTP::Response -MLWP::Simple -e ' ; # The
$d = new HTTP::Daemon and fork and getprint $d->url and exit;#spider
($c = $d->accept())->get_request(); $c->send_response( new #in the
HTTP::Response(200,$_,$_,qq(Just another Perl hacker\n))); ' # web
| [reply] [d/l] |
|
|
#include <stdio.h>
void main() {
int n;
for( n=0; n<10; n++ ) {
printf( "%d\n", n );
if( n == 5 ) break;
}
printf( "Residual value: %d\n", n );
}
__END__
P:\test>bcc32 test.c
Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland
test.c:
Turbo Incremental Link 5.00 Copyright (c) 1997, 2000 Borland
P:\test>test
0
1
2
3
4
5
Residual value: 5
And even in languages that allow the loop variable to be declared within the for loop initialiser statement, it's scope is not that of the for loop body, but that of the enclosing block.
From p.38 of "Programming in C++ by Stephen C. Dewhurst & Kathy T. Stark"
Declarations are statements and can appear any place a statement can within a block. This flexibility allows names to be declared and initialised at the point of their first use. For example, the first clause in a for loop control is a statement so a loop index can be declared there.
for( int i = 0; i < limit; i++ ) {
// etc.
}
return i;
The scope of the name extends from its point of declaration to the end of the enclosing block. Notice that in this example, i is still available after the loop terminates. It goes out of scope at the end of the block that contains the for statement.
I don't have a Pascal reference handy, but I seem to recall (from a very long time ago) that it's for loop acted similarly.
Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail
Hooray!
| [reply] [d/l] [select] |
|
|
I have absolutely no evidence here, but all your example proves is that specific implementation retains the value in the loop iterator. I don't have the C spec handy so I have nothing to prove that Corion is right, but I too seem to recall something along those lines, that the loop iterator is undefined after the loop and so it's best practice not to rely on it, regardless that it's defined for probably 95% of the compilers.
| [reply] |
|
|
|
|
|
|
After some searching, I found this link for Pascal, which tells what I know about Pascal, that the value is undefined after leaving the loop. I think that Turbo Pascal and Borland Pascal had the value of the upper bound of the loop at the end of the loop, but confusion started once they implemented optimizations in BP7 and Delphi.
For C, I browsed the standard a bit, but didn't stumble on anything going in either direction, so I guess the behaviour is neither implementation defined nor undefined but merely unspecified (some FAQ goes to some lengths to explain the differences between these three regarding the ANSI C standard...). I guess that one will find ugly differences soon enough if one uses enough different compilers and not only gcc...
The same pages as for the Pascal link give some remarks about C loops, but also no hint as to what the value of the loop iterator is after leaving the loop.
perl -MHTTP::Daemon -MHTTP::Response -MLWP::Simple -e ' ; # The
$d = new HTTP::Daemon and fork and getprint $d->url and exit;#spider
($c = $d->accept())->get_request(); $c->send_response( new #in the
HTTP::Response(200,$_,$_,qq(Just another Perl hacker\n))); ' # web
| [reply] [d/l] |
|
|
|
|
Please don't use void main. It's broken, just like double main or union {int x; float y; } **main.
The C standard says: main() must be declared as int main(void) or int main(int, char **) or equivalent (or in some implementation-defined manner, which is inherently unportable). See also http://users.aber.ac.uk/auj/voidmain.shtml.
| [reply] [d/l] [select] |
|
|
Re: Re: for loop localisation bug?
by demerphq (Chancellor) on Dec 29, 2003 at 18:54 UTC
|
Turing goes a step further and explicitly forbids the use of an iterator variable outside of the loop its used in. In that language you dont have to declare an iterator, and it disappears as soon as the loop terminates.
And I believe the reason for this logic is that the value can be undefined depending on how the compiler handles it. Ie:
for my $i (0..10) { print "$i\n" }
Could be optimised into ten print statements with no iterator at all.
---
demerphq
First they ignore you, then they laugh at you, then they fight you, then you win.
-- Gandhi
| [reply] [d/l] [select] |
|
|
my $i;
for $i ( 0 .. 10 ) { last if $i == 5; }
print $i;
This could equally be optimised to remove the loop, but optimising the iterator away would be an error. In this case, the modification of the iterator is the primary purpose of the loop.
Of course, this is grossly simplified, the break condition would not be a comparison against a constant, and in the original scenario, the iterator was being used as the index into an array. In other words, the loop was searching an array for a particular value and the desired outcome is the index of the array at which it is found.
This isn't exactly an unusual use of a for loop
How would this be done in Turing?
Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail
Hooray!
| [reply] [d/l] |
|
|
print 5;
As for Turing you would say (and I forget most of my turing so ill write in Perl as if it were Turing)
my $value;
for $i (0..10) {
if ($i==5) {
$value=$i;
last;
}
}
print $value;
Actually thats much the same as what you need to do in Perl anyway (if you are interested in the value of the iterator afterwards.) IMo, its arguable that you would be better served by a while loop anyway, at least thats what I usually do in this type of scenario.
Oh, Happy New Year mate. :-)
---
demerphq
First they ignore you, then they laugh at you, then they fight you, then you win.
-- Gandhi
| [reply] [d/l] [select] |
Re: Re: for loop localisation bug?
by Anonymous Monk on Dec 29, 2003 at 16:03 UTC
|
I believe the word is rationale | [reply] |