Using Filehandle and while Loop Defined
While reading a book on advanced Perl programming (1), I came across this code:
while (defined($s = <>)) {
...
Is there any particular reason for using it defined
here? the documentation for perlop says:
In these loop constructs, the assigned value (whether the assignment is automatic or explicit) is then tested to determine if it is defined. a certain test avoids problems when a string has a string value that will be treated as false by Perl, such as a
""
or a"0"
new line without trailing. If you really want such values ββto end the loop, they must be checked explicitly: [...]
So, would it be an angle or just because the book is too old and an automated test defined
was added to a recent version of Perl?
(1) Advanced Perl Programming, First Edition, by Sriram Srinivasan. O'Reilly (1997)
source to share
Perl has a lot of hidden behavior, much more than most other languages. Perl's motto is more than one thing, and because such latent behavior exists, there is often more than one way. Exactly the same thing.
/foo/
instead$_ =~ m/foo/
$x = shift
instead$x = shift @_
while (defined($_=<ARGV>))
insteadwhile(<>)
and etc.
Which expressions to use mainly depends on your local coding standards and personal preference. More explicit expressions remind the reader of what's really going on under the hood. This can improve or improve the readability of your code - it depends on how knowledgeable the audience is and whether you are using well-known idioms.
In this case, the implicit behavior is a little more complicated than it sounds. Sometimes it perl
implicitly tests defined(...)
the result of a readline statement:
$ perl -MO=Deparse -e 'while($s=<>) { print $s }'
while (defined($s = <ARGV>)) {
print $s;
}
-e syntax OK
but sometimes it won't:
$ perl -MO=Deparse -e 'if($s=<>) { print $s }'
if ($s = <ARGV>) {
print $s;
}
-e syntax OK
$ perl -MO=Deparse -e 'while(some_condition() && ($s=<>)) { print $s }'
while (some_condition() and $s = <ARGV>) {
print $s;
}
-e syntax OK
Let's assume you're worried about the corner cases that this implicit behavior should handle. Have you passed perlop
in memory so you know when Perl uses this implicit behavior and when it doesn't? Do you understand the differences in this behavior between Perl v5.14 and Perl v5.6? Do the people reading your code know?
Again, there is no right or wrong answer about when to use more explicit expressions, but the case of using explicit expressions is stronger when implicit behavior is more esoteric.
source to share
Let's say you have the following file
4<LF>
3<LF>
2<LF>
1<LF>
0
( <LF>
represents a string. Note the absence of a newline on the last line.)
Let's say you are using the code
while ($s = <>) {
chomp;
say $s;
}
If Perl hadn't done anything magic, the output would have been
4 3 2 1
Note the absence 0
, since the string 0
is false. defined
necessary in the unlikely event that
- You have a non-standard text file (trailing newline missing).
- The last line of the file consists of one ASCII zero (0x30).
BUT WAIT FOR MINUTES! If you actually ran the above code with the above data, you will see it 0
printed! Many people don't know that Perl will automatically convert
while ($s = <>) {
to
while (defined($s = <>)) {
as shown here:
$ perl -MO=Deparse -e'while($s=<DATA>) {}'
while (defined($s = <DATA>)) {
();
}
__DATA__
-e syntax OK
So technically it doesn't even need to be specified defined
in this particular case.
However, I can't blame anyone for being explicit instead of relying on Perl to automatically modify their code. After all, Perl is (necessarily) quite specific about which code sequences it will change. Note the lack of defined
the following, although it is supposedly equivalent code:
$ perl -MO=Deparse -e'while((), $s=<DATA>) {}'
while ((), $s = <DATA>) {
();
}
__DATA__
-e syntax OK
source to share