davido has asked for the wisdom of the Perl Monks concerning the following question:
I just love this kind of thing because I have to think my way through unravelling the chain of events leading to a funky warning. Yesterday I was reading Modification of a read-only value, and specifically Aristotle and ysth's comments, and that got me to thinking...
I guess in the back of my mind I always knew that reading from a filehandle implicitly into $_ doesn't localize $_. That fact didn't really ring any alarms in my mind with respect to how that would affect aliasing of $_ in foreach loops, probably mostly because I almost always name my iterator with a lexical, and almost always name the recipient variable of filehandle reads, so I really never run the risk of this becoming a problem. But I wanted to experiment anyway.
The background: Assume you've got a foreach loop set up, and you haven't specified an iterator. That means the iterator will be $_ implicitly. $_ will be aliased to each element (one at a time) of whatever list you're iterating over. Fine. Now assume that within that foreach loop, you're reading from a filehandle, and you're not specifying a named recipient variable (in other words, you're not saying my $line = <FH>;..., you're just saying <FH> and letting Perl plop the line into $_. Is this starting to raise any red flags? It should, of course.
But I couldn't leave well enough alone, and decided to do just that to see if the behavior was what I expected. ...my expectations aren't always dead on target when dealing with some of the less apparent constructs, so a little test was in order.
Consider the following code:
use strict; use warnings; my ( @array ) = qw/ one two three four /; my $position = tell DATA; print "\@array holds @array.\nPrinting DATA a few times:\n"; foreach( @array ) { while ( <DATA> ) { chomp; print $_, "\t"; } print "\n"; seek DATA, $position, 0; } print "Array: @array\n"; __DATA__ a b c
Now it doesn't surprise me that I got a warning or four running this script. But what does surprise me is the warning I got:
@array holds one two three four. Printing DATA a few times: a b c a b c a b c a b c Use of uninitialized value in join or string at mytest.pl line 20, <DA +TA> line 12. Use of uninitialized value in join or string at mytest.pl line 20, <DA +TA> line 12. Use of uninitialized value in join or string at mytest.pl line 20, <DA +TA> line 12. Use of uninitialized value in join or string at mytest.pl line 20, <DA +TA> line 12. Array:
Lets unravel what's going on here...
First, we assign the values 'one', 'two', 'three', and 'four' to @array. Next, we iterate over @array, using $_ as the implicit aliased iterator. Within each iteration we read from <DATA> until there's nothing more to read. On each iteration, $_ is assigned the contents of the filehandle read. Remember, $_ is aliased back to an element of @array on each iteration of the foreach loop. So reading into $_ is going to modify elements of @array. After the last iteration of the while loop, <DATA> is checked one more time for stuff, and nothing is found, so undef is returned. $_ will be assigned undef on the final conditional check for the while loop (I think), and that means that for each iteration over the elements of @array, that element will receive a value of undef.
That's what I expected. What I didn't expect is that the warnings I got would refer back to <DATA> when I print @array. Yes, we're trying to join four uninitalized values (actually four undefs, but that's the same thing). But why is the warning reporting that I'm on <DATA> line twelve? The print statement didn't perform a read on <DATA>. The only thing I can think is that warnings might always specify what line of the most recently used, still opened filehandle is the current file line, even if the event triggering the warning didn't just read from the filehandle.
Dave
|
|---|