http://qs1969.pair.com?node_id=56469

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

Replies are listed 'Best First'.
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!

      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

        Even than:
        for (@ARGV){ print if s/first/somet/ or s/second/somet/; }
        Would use only one print ;-).

        Jeroen
        "We are not alone"(FZ)

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"; } }
      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. :)
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]
      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.

        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.