Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Localization gotcha?

by benizi (Hermit)
on May 19, 2005 at 01:03 UTC ( [id://458480]=perlquestion: print w/replies, xml ) Need Help??

benizi has asked for the wisdom of the Perl Monks concerning the following question:

I've conditioned myself at this point to drop in a 'local $_;' whenever I use the <> operator in a function. But, today I was bitten by an oddity of $.. It was in very non-production code, but it's still worth knowing what's going on.

A simple example (what to look for: the line numbers in the warnings):

#!/usr/bin/perl use strict; use warnings; { open my $fh, '<', 'somefile' or die "Opening somefile for reading: + $!"; sub seeky { seek $fh, 0, 0 } } while (<DATA>) { chomp; my $line = $.; warn "Pre match: Line $. == $line\n"; next if /match/; seeky(); warn "Postmatch: Line $. != $line\n"; } __END__ foo match bar baz match bot

From perldoc perlvar, I think the following bit me:

When a line is read from a filehandle (via readline() or <> ), or when tell() or seek() is called on it, $. becomes an alias to the line counter for that filehandle.

I have two questions:

  • Why should I have expected this behavior? $fh isn't even visible from the scope in which I use $.
  • What other examples of these kinds of "gotchas" when using predefined variables can people provide?

(UPDATE): Also, I noticed the section from perldoc perlvar about localizing $.:

Localizing $. will not localize the filehandle's line count. Instead, it will localize perl's notion of which filehandle $. is currently aliased to.

... but I couldn't seem to elicit any helpful effects by doing so. (figured a local $.; after the while(<DATA>) might keep it associated with *DATA.)

Replies are listed 'Best First'.
Re: Localization gotcha?
by davido (Cardinal) on May 19, 2005 at 02:05 UTC

    The special variable '$.' is convenient when you want a quick way to detect the line number of the currently read file. But it can get a little confusing when you're dealing with multiple input files. In such cases, it might be advisable to take advantage of the added control and comfort of an object-oriented interface to filehandles such as that provided by IO::File.

    IO::File inherits from IO::Handle and IO::Seekable. And one of the methods of IO::Handle is: input_line_number(), which happens to be an object-encapsulated substitute for '$.', but benefitting from all the scoping that applies to object instances.

    A usage example:

    use strict; use warnings; use IO::File; { my $fh = IO::File->new( '< filename.txt' ); if( defined $fh ) { while( my $line = <$fh> ) { print $fh->input_line_number(), ":\t", $line; } } } # As $fh falls out of scope it is closed automatically.

    It seems that input_line_number() is probably somewhat of a misnomer. Given that the input record separator needn't be '\n', the method would probably be better named input_record_number(), but that's just water under the bridge. :)


    Dave

      Thanks for the suggestion, though I solved my actual problem per the response below (localizing $. wherever my module performed a seek() or used <>).

      Instead of using IO::File as you propose, since open()'ed files are automatically IO::Handles, you can just "use IO::Handle;" to provide the input_line_number method (note the continue):

      use strict; use warnings; use IO::Handle; while (<>) { my $lineno = ARGV->input_line_number; # print a status message every 100 lines warn "Processing $ARGV line $lineno\n" unless $lineno % 100; # do some processing stuff } continue { close ARGV if ARGV->eof; # else input_line_number won't reset per- +file }

      (Update:) s/eof/ARGV->eof/ in the code example. (else a poorly written function, like my original seeky() would let the eof refer to the wrong file)

Re: Localization gotcha?
by Animator (Hermit) on May 19, 2005 at 10:10 UTC

    local $. in the <DATA>-block has no usage. What will perl do? it will copy the current value of $. to a temp variable, and reset it after the block.

    It is not this block that it is messing up the results of $., it is the seeky-sub. What you could do is add a local $. in sub seeky, then you get the results you expect.

    Since this means that at the start of the block it will copy $. to another variable, seek will set $. to another alias, and at the end of the block the value of $. is restored (by using the copy it made).

    Update: another solution with which you would get the same result is to create a new block in your code, and localising $. in there. Something like:

    while (<DATA>) { chomp; my $line = $.; warn "Pre match: Line $. == $line\n"; next if /match/; { local $.; seeky(); } warn "Postmatch: Line $. != $line\n"; }

      You're quite right. And even though I understood what was going wrong, my interpretation of localization was backwards in this case. I was thinking that I was trying to make $.'s behavior local to the <DATA>-block, when I should have been attempting to localize seek()'s effects on $. to seeky().

      (It didn't help matters that my original problem was significantly more complicated. I tried to fix it by localizing $. wherever I had a "local $_;" in a module, but forgot to search for seek()s.)

      FWIW, I would add the "local $.;" statement to seeky itself, analagous to adding "local $_;" in subroutines.

      Thanks for the response, Animator.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://458480]
Approved by graff
Front-paged by bart
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (3)
As of 2024-04-25 19:49 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found