Re: Doubt on defined-ness
by almut (Canon) on Jun 18, 2008 at 19:06 UTC
|
So why is the first idiom "suboptimal"?
I haven't looked up that passage in the Camel Book, so I'm not sure
what it's referring to... but maybe it's trying to point out that 'if ($_=<FH>) ...' could
evaluate to false in circumstances where you don't want it to be false (the line being empty or zero). Just a guess though.
(Update: crossed out "empty", because - upon rethinking - I can't
really come up with any case where $_ would be the emtpy string —
not even when fiddling with the input record separator...)
Update 2: ikegami actually did find a variant
where <> can return an empty string... So, I might in theory
un-strike that part again. But I won't — I guess it's unlikely anyone will run
into this case by accident :)
| [reply] [d/l] [select] |
|
|
>perl -MData::Dumper -e"while (<{,0,a}>) { print Dumper $_ }"
$VAR1 = '';
$VAR1 = '0';
$VAR1 = 'a';
A tied handle could do it too, say one that autochomps.
| [reply] [d/l] [select] |
|
|
Maybe on a non-blocking handle?
| [reply] |
|
|
#!/usr/bin/perl
use Fcntl;
fcntl(STDIN, F_GETFL, my $flags) or die "Couldn't get flags: $!\n";
fcntl(STDIN, F_SETFL, $flags|O_NONBLOCK) or die "Couldn't set flags: $
+!\n";
$_=<STDIN>;
use Data::Dumper;
print Dumper $_; # --> prints $VAR1 = undef;
Maybe you can construct a better case (?)
| [reply] [d/l] |
|
|
|
|
|
|
I remember I brought up that bug, back in the days that defined was not yet implied...
If your text file ended with a "0" on a line of its own, without appended newline, that last line was ignored.
Worse: if that happened in a file in the middle of a list of files in @ARGV, then while(<>) was interrupted at the end of that file, and the rest of the files that came after it were simply ignored.
As for the Camel Book: historically, originally defined was not implied. The text in the book probably just still reflects that.
| [reply] [d/l] |
Re: Doubt on defined-ness
by pc88mxer (Vicar) on Jun 18, 2008 at 19:07 UTC
|
The condition will be false if $_ is undef or 0 or the empty string (or whatever else perl deems to evaluate to false.)
So, if your last line is not terminated by a newline and is the string "0", you'll get tripped on this if don't for defined-ness.
Update: Considering the discussion below, I should have made it clear that this is a potential problem only for if statements (which is what the OP asked about), not while statements.
What is interesting is that even though the condition expression for while and if statements is executed in "boolean" context, the magic of the diamond operator only happens in while statements.
use strict;
use warnings;
use Contextual::Return;
use Carp;
while (foo()) { last; }
while (my $x = foo()) { last; }
if (foo()) { }
if (my $z = foo()) { }
sub foo {
return
BOOL { carp "BOOL"; 1; }
SCALAR { carp "SCALAR"; 1; }
;
}
__END__
BOOL at ./cr line 6
BOOL at ./cr line 7
BOOL at ./cr line 8
BOOL at ./cr line 9
| [reply] [d/l] [select] |
|
|
Actually I was about to answer just that, but then I tested it - and it turned out I was wrong.
Consider this:
$ hexdump -C foo
00000000 61 0a 30 |a.0|
00000003
$ perl -wle 'print "read line" while (my $x = <>)' foo
read line
read line
$
I have a file foo here with an 'a', a newline, and a '0' - no trailing newline.
I'd expect just one line of output, because 0 is false. But why am I getting two lines? That doesn't seem to be logical to me.
So I modified my test scrpt a bit:
$ perl -MDevel::Peek -wle 'while (my $x = <>){ print "read line"; pri
+nt "and its true" if $x }' foo
read line
and its true
read line
So, the second line evaluates to true in the while-condition, but not in the following if-condition. Is there some kind of special magic? If yes, where is it documented?
Update ok, got it. Thank you all. I didn't realize it works for arbitrary variable names, and thought that $_ was special cased. | [reply] [d/l] [select] |
|
|
It's because perl applies its magic to your while. Deparse shows it as
while (defined(my $x = <ARGV>))
| [reply] [d/l] |
|
|
It's just syntactic sugar. Perlop says:
The following lines are equivalent:
while (defined($_ = <STDIN>)) { print; }
while ($_ = <STDIN>) { print; }
while (<STDIN>) { print; }
| [reply] |
|
|
So, the second line evaluates to true in the while-condition, but not in the following if-condition. Is there some kind of special magic? If yes, where is it documented?
the only location where i found it mentioned after a quick search is in perlvar $_:
$ARG
$_ The default input and pattern-searching space.
The following pairs are equivalent:
while (<>) {...} # equivalent only in while!
while (defined($_ = <>)) {...}
edit: and in perlsyn
edit: ah, it's in perlop, like betterworld mentioned. overlooked it. | [reply] [d/l] |
|
|
Well, ok for the non-newline terminated lines (I hadn't thought about that case), but can really $_ be undefined after $_=<FH> if we are not at eof?
| [reply] |
Re: Doubt on defined-ness
by FunkyMonk (Bishop) on Jun 18, 2008 at 19:18 UTC
|
while (defined($_ = <FH>))
Which should clear up the other half of your confusion.
| [reply] [d/l] [select] |
|
|
The camel book lists the following (among others) as being equivalent:
while (defined($_=<STDIN>)) ...
while ($_=<STDIN>) ...
while (<STDIN>) ...
so I guess that only in the case of a while both the second and third form are equivalent to the first. This is not true for other boolean context like eg if.
| [reply] [d/l] |
|
|
It seems perl applies its magic test for definedness to any while that involves a filehandle (at least, I couldn't find a case where it didn't apply).
$ perl -MO=Deparse -e 'while (<>) {} while ( <ARGV> ) {} while ( $_ =
+<> ) {} while ( $x = <> ) {}'
while (defined($_ = <ARGV>)) {
();
}
while (defined($_ = <ARGV>)) {
();
}
while (defined($_ = <ARGV>)) {
();
}
while (defined($x = <ARGV>)) {
();
}
-e syntax OK
| [reply] [d/l] |
|
|