mkmcconn has asked for the wisdom of the Perl Monks concerning the following question:
Fellow-seekers of Perlish righteousness, lately I have
been dabbling with using and and or in places for which I cannot
prove precedence among the fathers.
Tell me, am I sinning to use and and or to combine statements
as follows?
for my $next (@ARGV){
opendir(PWD,"$next") and
my @files = readdir PWD or
die "Open \"$_\" failed: $!";
closedir PWD or die $!;
print "$_\n" for @files;
}
Especially, this seems useful in testing regular expressions
as in this recent example, where ...
s/test/whatever/ and print or print;
...will print the substitution if the match succeeds, or print
the unmodified string if it fails.
Are there problems with this style? Is it
testing what I think it is? Is it always going to
be reliable, or will it break under some conditions? Is it
more common to do this than I had thought?
mkmcconn
Re: and this or that
by jeroenes (Priest) on Feb 05, 2001 at 22:57 UTC
|
And & or have the lowest priority possible, according to
perlop, so you are safe. The last few from the row:
nonassoc list operators (rightward)
right not
left and
left or xor
You see, the list operator is even higher, and that is
the most tricky one out there.
I wouldn't use and with substitution, though. Just:
s/test/somestuff/;
print;
does the job cleaner: only one print.
Hope this helps, Jeroen
"We are not alone"(FZ)
Update: lemming showed me a typo. Fixed.
Thanx! | [reply] [d/l] [select] |
|
Thank you for the reply jeroenes. (BTW, the first block in
my question has a mistake, saying "$_" where it should say
"$next".)
You said
I wouldn't use and with substitution, though. Just:
s/test/somestuff/;
print;
does the job cleaner: only one print.
You're right about that. Would the following be a better illustration of
the advantage I perceive?
s/^(\d+)/("0"x(8-length).$1)/xe and print or
s/^(\w)/("\x20"x(8-length).$1)/xe and print for @ARGV;
Suppose that @ARGV is a combination of digits and words,
all digits get zero-padding and all words are padded by
space. Anything that fails both matches should be discarded.
Update:
Very well then, jeroenes - I will follow the tradition
And thanks again for the replies :-)
mkmcconn | [reply] [d/l] [select] |
|
for (@ARGV){
print if s/first/somet/ or s/second/somet/;
}
Would use only one print ;-).
Jeroen
"We are not alone"(FZ) | [reply] [d/l] |
Re: and this or that
by dws (Chancellor) on Feb 05, 2001 at 23:40 UTC
|
Your precedence problem goes away with a little untangling. Move the error handling for opendir() in closer to the call, rather than out past the readdir(). Here's I'd code it.
for my $next (@ARGV){
opendir(PWD, "$next") or die "$next: $!";
my @files = readdir(PWD) or die "$next: $!";
closedir(PWD);
print join("\n", @files), "\n";
}
(Updated to add error handling after readdir(), too.)
Or, if you really want to use precedence, and if you want to ignore non-directories in @ARGV, consider
for my $next (@ARGV) {
opendir(PWD, "$next") and do {
my @files = readdir(PWD) or die "$next: $!";
closedir(PWD);
print join("\n", @files), "\n";
}
}
| [reply] [d/l] [select] |
|
for my $next (@ARGV) {
opendir(PWD, "$next") and do {
my @files = readdir(PWD) or die "$next: $!";
closedir(PWD);
print join("\n", @files), "\n";
}
}
TIMTOWDI. I personally find this a little clearer, and is no less idiomatic:
foreach my $next (@ARGV) {
if (opendir(PWD, "$next")) {
my @files = readdir(PWD) or die "$next: $!";
closedir(PWD);
print join("\n", @files), "\n";
}
}
While using and and do like that is only slightly less clear than using a true 'if' block, it is slightly less clear than using a true 'if' block. :) | [reply] [d/l] [select] |
Re: and this or that
by MeowChow (Vicar) on Feb 06, 2001 at 00:33 UTC
|
Usually if I want to do something like this, I use both the low-precedence and high-precedence operators, to explicitly and obviously indicate what is happening, as follows:
opendir(PWD,"$next") && my @files = readdir PWD
or die "Open \"$_\" failed: $!";
A side note: As a matter of good form, I don't declare lexically scoped variables within subexpressions, and especially not within conditionally executed ones, like you did with @files. In this specific example, I would not combine the readdir and openddir in one line.
Your second example I would seperate into two statements as jeroenes suggested above, but you can also do
s/test/whatever/, print;
if you're really itching to put it all on one line. Or, if you prefer:
s/test/whatever/ => print;
MeowChow
print $/='"',(`$^X\144oc $^X\146aq1`)[-2] | [reply] [d/l] [select] |
|
Aside from the minor problem of using $_ where you mean to use $next, there are two problems with
opendir(PWD,"$next") && my @files = readdir PWD
or die "Open \"$_\" failed: $!";
First, you waste future readers' time. The few seconds you've saved by writing the above rather than
opendir(PWD,$name) or die "$name: $!";
my @files = readdir(PWD) or die "$name: $!";
Have just eclipsed by N x "what's this? uh.. oh, I see". Better, in my humble opinion, to write straightforward code. Sure, you have code "or die" twice, but in the scheme of things, that's a really minor nit.
Second, in the unikely event that opendir() succeeds and readdir() fails, you're presenting a misleading diagnostic.
| [reply] [d/l] [select] |
|
Which is exactly why I said:
"In this specific example, I would not combine the readdir and openddir in one line. "
I was re-cycling his example code simply to make another point about operator precedence.
| [reply] |
|
|
|