in reply to What Is Going On Here ?????
The foreach loop will always localize its loop variable (That is $_ unless one is spesified). This can easily be verified:
This will print$_ = 42; foreach (1, 2, 3) { print "Inside loop: \$_ = [$_]\n"; } print "Outside loop: \$_ = [$_]\n";
Inside loop: $_ = [1] Inside loop: $_ = [2] Inside loop: $_ = [3] Outside loop: $_ = [42]The while loop however, does not localize anything. This can be seen from the following example
If <FH> produces three lines containing the digits 1, 2 and 3 the output will be$_ = 42; while(<FH>) { print "Inside loop: \$_ = [$_]"; } print "Outside loop: \$_ = [$_]\n";
Inside loop: $_ = [1] Inside loop: $_ = [2] Inside loop: $_ = [3] Outside loop: $_ = []It will also produce a warning if you remembered to turn on the -w flag. Why the warning you might ask. The reason is because the <FH> will return undef when it has reached the end of the file, and $_ is assigned this undef. And as we all know, perl will warn us if we use an undefined value (if the -w flag is used).
So then, what is it that happens in your program ? First, let's just simplify it a little bit (concentrating on the first foreach loop):
Now, what happens is that the foreach loop creates spesial references into the @test_array. (Spesial in the sense that when you change $_, you also change the corresponding element in the @test_array)foreach (@test_array) { print qq|Before while: [$_]\n|; open(IN, $0) || die "No file: $!\n"; while (<IN>) { } close IN; print qq|After while: [$_]\n|; }
The next line should then print out the value of $_, which in your example would be "Element1". Then we open ourself, and loop over each line in the file. The while (<IN>) construct implicitply assigns the $_ without first localizing it. Thus we actually change the elements of the array @test_array.
If you instead had used a foreach loop to iterate over the file, this problem would not have occured, because of the fact that it would have localized the $_ variable for you. Or you could localize $_ yourself by writing local $_. Another possible solution is to use lexical variables both in the foreach loop and in the while loop
(The defined test isn't really necessary in newer perls, but I like to be explicit here).foreach my $elem (@test_array) { open(IN, $0) || die "No file: $!\n"; while( defined(my $line = <IN>) ) { } close IN; }
"But", I hear you complain, "my while loop is in a different function, a completly new scope". Well, that is correct if you are talking about lexical scoping (variables declared with the my keyword are lexical variables). However, $_ is a dynamically scoped variable (this fact you can't change, you can't declare my $_. Here is a good tutorial explaining the difference between a lexical scope and a dynamic scope.) However, since $_ is dynamically scoped, it will live on in the new function only to be assigned by the while loop.
I hope this shed some light on the problem at hand.
Autark.
|
---|