2

I'm new to Perl and I have a problem when reading a file line by line. I started with a tutorial which suggested using a while loop. That worked fine, however I wanted to have an option to break out of the loop in case of an error. I know I can use the "last" keyword, but I don't like that very much, I would like to include it in the while loop's boolean expression so it is easier to see on what conditions it stops. So I did

$error=0;
while ( (!$error) && (<MYFILE>) ) {
print $_;
...    
}

Unfortunately, this doesn't work because $_ seems to contain a "uninitialized value". For some reason, when I change (!$error) to (!0), it works again. I would understand that it was not working had I used || because of lazy evaluation, but with && in this case both sides need to be evaluated so I don't understand why it doesn't initialize the line variable.

3
  • 6
    Learn the idioms of the language you are learning (any language) - and last is one of the idioms of Perl. Commented Aug 25, 2010 at 14:33
  • 1
    As I have stated in the question, I know about the last keyword. Commented Aug 25, 2010 at 14:48
  • 1
    Okay then, learn to embrace the idioms of the language. Commented Aug 25, 2010 at 21:22

2 Answers 2

5

The magical assignment to $_ only occurs if there is nothing else in the loop:

while (<MYFILE>) {
    print $_;
    ...    
}

If you want the error test too - which is not recommended - then you need to do the assignment:

my $error = 0;
while (!$error && ($_ = <MYFILE>)) {
    print;
    $error = 1 if m/quit/i;
}

Don't forget to add:

use warnings;
use strict;

The reason why the version with '!0' worked is probably because Perl optimizes and recognizes that '!0' is always true and removes it from the loop, leaving just the I/O operator, which then assigns to $_.

NB: it is far better to use idiomatic Perl and the last statement:

while (<MYFILE>) {
    print;
    last if m/quit/i;
}
Sign up to request clarification or add additional context in comments.

4 Comments

I believe you need to add a "defined" check on $_ in the while loop condition, otherwise a line in the file containing "0" will evaluate as false and end the loop. So, something like: while (!$error && defined ($_ = <MYFILE>)) { ...
Ok, thanks, I didn't know about the nature of the loop. I thought it would improve readability if all conditions for the loop are in one place. At university, I was taught GOTO and - to a lesser extend - break statements are evil, so I have always tried to avoid them. But I will use the version you suggested.
@Matthew: no, you don't need to do that. The line also contains a newline (to be removed by chomp when required). I suppose if a file ends with newline followed by 0 (and no trailing newline), then you might be correct - but that's not a standard text file.
@Matthew Wilson: The defined check is done automatically. See perldoc.perl.org/perlop.html#I/O-Operators
3

First, as Jonathan points out you should always write languages idiomatically unless you have a very good reason and here you don't.

Your larger problem is misunderstanding the while loop. The automatic assignment of the result of the angle operator to $_ only occurs if it is the only thing in the while conditional test. If there are multiple items in the conditional test they are evaluated and then discarded (Programming Perl pp. 80-81 and I/O Operators at Perldoc).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.