bofh_of_oz has asked for the wisdom of the Perl Monks concerning the following question:

I am not sure that what i'm thinking of doing is possible/desirable and would like your opinion on this...

Is it possible to mix the and, or, if, unless etc in one statement, something like this:

A unless B or (C and D) if (E and F)
Also, if it is possible, I would like your somments on whether it should be done this way, or maybe I should follow "the C way" of if{}else{}...

--------------------------------
An idea is not responsible for the people who believe in it...

Replies are listed 'Best First'.
Re: Complex conditional statements
by ikegami (Patriarch) on Jun 16, 2005 at 14:19 UTC

    What do "if" and "undef" mean? What's the relative precedence of "unless", "or" and "if"? If your snippet is equivalent to

    if (not B) { A } else { if (E and F) { C and D } else { undef } }

    it can be written as

    (not B and A) or (B and ( ((E and F) and (C and D)) or (not (E and F) and undef) ))

    and simplified (through logic arithmetic) to

    (not B and A) or (B and E and F and C and D)

    Notes:

    For convertion to single expressions: if (A) { B } else { C } === (A and B) or (not A and C) For simplification: X and undef === undef X or undef === X
      A great explanation, thanks a lot!

      It raised another question, though: if one or more of those statements (A, B etc) is a print "..." statement, how can I make it print only if B is false (as in not B and A)?

       
      UPDATE: Here's how my test code looks:

      $test = $comment = 1; ($a = 3 and print "--> $a\n") if ($comment or $test) or print "no way! +\n";

      It works if ($comment or $test) gives true, but both print statements are executed if it gives false...

      --------------------------------
      An idea is not responsible for the people who believe in it...

        not B and print("...")

        Unlike C, every Perl function can be used in an expression, even those that don't normally return a value. But that's moot since print actually does return a value: true on success, false (and sets $!) otherwise.

        Keep in mind that using these techniques is almost guaranteed to make your code less readable or unreadable. For expressions of that magnitude, I encourage you to treat this discussion as an academic excercise.

        Update: Answer to Updated question:

        ($a = 3 and print "--> $a\n") if ($comment or $test) or print "no way!";
        is the same as
        ($a = 3 and print "--> $a\n") if ($comment or $test or print "no way!");
        Keep in mind that print pretty much always returns true, so the above doesn't do what you want. You could fix it by changing the return value of print:
        ($a = 3 and print "--> $a\n") if ($comment or $test) or (print("no way!"), 0);
        But that's aweful code! What's wrong with
        if ($comment or $test) { $a = 3; print "--> $a\n"; } else { print "no way!\n"; }
        You could shorten it a little to
        $comment or $test ? ( $a = 3, print "--> $a\n" ) : print "no way!\n";
        but you're starting to mess with people's ability to read your code if you do that.

Re: Complex conditional statements
by tlm (Prior) on Jun 16, 2005 at 14:16 UTC

    You can always test something like that with a one-liner.

    % perl -wle '1 unless 2 or (3 and 4) if (5 and 6)' syntax error at -e line 1, near ") if" Execution of -e aborted due to compilation errors.
    On the other hand, statement modifiers at the end are syntactically OK; e.g. 1 or 2 if 3.

    Whether it is good programming is less clear-cut.

    I like using statement modifiers rather than regular if-else blocks whenever the contents of the blocks would have been short. I.e.

    bar() if $foo;
    rather than
    if ( $foo ) { bar(); }
    And sometimes I stretch it to statements like
    bar() or baz() if $foo;
    in preference over
    if ( $foo ) { unless ( bar() ) { baz(); } }
    ...but I suspect this is already inching into obfuscation.

    Great question++, BTW. I look forward to reading the responses you get.

    the lowliest monk

Re: Complex conditional statements
by Transient (Hermit) on Jun 16, 2005 at 14:11 UTC
    I think that's difficult to understand, even in English.

    I would definitely recommend
    if !B then A else if (E and F) then (C and D)
    Makes more sense to me <shrug>

    On another note, I did try to make that work in a single statement and couldn't... but that doesn't prove anything.
      I think that's difficult to understand, even in English.

      Guess it's my Russian background and non-linear thinking then. ;)

      As for the

      if !B then A else if (E and F) then (C and D)
      AFAIK there is no then keyword in Perl, and I wanted to use the corerct syntax (i.e. to be able to replace A, B, C etc by valid statements to get a working code)

      By the way, where can I find references to the syntax of those Perl keywords? I tried to search for them, but they aren't functions, and Google promptly omits them from the search criteria...

      --------------------------------
      An idea is not responsible for the people who believe in it...

        oh, well, then you're still missing curly braces and mandatory parens :) I was equating to English. But I really meant "spoken language" when I said English... but perhaps that is more of a characterstic of the way English is constructed? I know for me, I had to re-read your original statement a few times to fully grok the meaning. It seems more natural as two separate concepts (personally speaking).

        Code-wise you want something like (if A, B, C and D were variables):
        if ( !$b ) { $a; } elsif ( $e and $f ) { $c and $d; }
        if they are statements, something similar should suffice, with perhaps more parens and/or do {} blocks. HTH
        By the way, where can I find references to the syntax of those Perl keywords? I tried to search for them, but they aren't functions, and Google promptly omits them from the search criteria...

        `perldoc perlop`

        Replace then with &&.

        IE if ( A ) { B } is the same as A && B. Likewise if ( !A ) { B } is the same as A || B.

        So to code if !B then A else if (E and F) then (C and D) you would say

        ( ! B ) ? A : ( ( E && F ) && ( C && D ) )

      It is possible, take a look at this piece of code:
      $status != $sigsegv or $ret = $counter + 4 and printf("> Done!\n$bugfi +le is vulnerable at $counter bytes!\n") and last;
      As you can see i have mixed "or","and", and could include some "if" statement just for example:
      $status != $sigsegv or $ret = $counter + 4 and printf("> Done!\n$bugfi +le is vulnerable at $counter bytes!\n") and last if $test eq 1;
      A unless B or (C and D) if (E and F)
      and
      I think that's difficult to understand, even in English. I would definitely recommend if !B then A else if (E and F) then (C and D)

      Demonstably difficult to understand, because I read it more like:

      if (E && F) { unless (B || (C && D)) { A } }
Re: Complex conditional statements
by jhourcle (Prior) on Jun 16, 2005 at 16:25 UTC

    Yes, you can get it all into one statement, but I think that ikegami's answer is much more legible, easier to understand, and thus, easier to maintain.

    But based on the order of precedence from ikegami:

    if (not B) { A } else { if (E and F) { C and D } else { undef } }

    You could use:

    ( ! B ) ? A : ( ( E and F ) ? ( C and D ) : undef );

    or for legibility:

    (! B ) ? A :( (E and F) ? (C and D) : undef );

    or the completely horrible line noise:

    !B?A:E&&F?C&&D:undef;
Re: Complex conditional statements
by Tanktalus (Canon) on Jun 16, 2005 at 15:06 UTC

    You can't have more than one statement modifier (if, unless, for, foreach, while, until, .. am I missing any?) at a time. One reason you can't is that trying to figure it out in one's head is next to impossible, so trying to get a computer to do it in a way that users (perl programmers) expect would be a futile excersise.

    Which is about the same reason why I think perl 6 still doesn't allow it.

    Use more grouping constructs. In general, that would mean parenthesis, but in this case it means braces.

    I'd offer an alternative, but I can't quite get my head around what you perceive the precedence of the unless and if are to each other.

      Sorry, should've made myself more clear...

      ikegami has correctly decrypted the logic of that statement a few posts higher. However, it is just a test piece...

      What i'm trying to accomplish is: i have a script that is supposed to transfer files from ftp to certain directories (on WinNT/XP platform). The script has been failing randomly when downloading the files, with the error message saying "Bad file descriptor", which can mean almost anything. The files are there every time the transfer occurs, whether success or failure.

      I'm trying to introduce a --test option to the script, that would do a "dry run", without actually transferring the file, just printing status messages on every step. Since the majority of the code is the same for test and normal run, I want to use those if, and, or, unless keywords, and I want to keep the code compact. I have a C background, so my code did look like a C program... well, initially it did. But I'm realy impressed with the power of Perl to mangle several statements together like this, and trying to use that.

      If there are better ways to do this, I'm open to suggestions...

      --------------------------------
      An idea is not responsible for the people who believe in it...

        I'm trying to introduce a --test option to the script, that would do a "dry run", without actually transferring the file, just printing status messages on every step. Since the majority of the code is the same for test and normal run, I want to use those if, and, or, unless keywords, and I want to keep the code compact. I have a C background, so my code did look like a C program... well, initially it did. But I'm realy impressed with the power of Perl to mangle several statements together like this, and trying to use that.
        Now the problem is clear. You have a simple requirement that can be expressed clearly in code. However, giddy with the newly-discovered expressive power of Perl, you're looking for a way to squeeze what you want to say into as small a space as possible.

        Don't. It makes code ugly, complicated, and damned near impossible to understand, never mind maintain. It makes people hate Perl programmers and hate Perl. It's a fun game, and can even teach you something about the language, but you should never do it on production code (defined as code you will run more than once).

        In addition to which, the specific trick you're trying to use is not allowed. Statement modifiers are limited to one per statement.

        If there are better ways to do this, I'm open to suggestions...
        ikegami has the right idea:
        if ($comment or $test) { $a = 3; print "--> $a\n"; } else { print "no way!\n"; }
        By the way, where can I find references to the syntax of those Perl keywords? I tried to search for them, but they aren't functions, and Google promptly omits them from the search criteria...
        They are documented in perlsyn.

        To be honest, I think the better way to do it really is to not use those keywords (at least, not as statement modifiers), and not to keep the code compact. Whitespace is your friend. Especially after six months.

        I don't know if you noticed it, but most of the people who have given analysis of your "complex conditional statement" (to use the term you used in the subject line) have added newlines and other whitespace just so they could wrap their heads around what was going on, and to help explain the logic that they were assuming you meant. This is a good indicator that your code, if you want it easy to read, decipher, and analyse later, should do similarly: add newlines and whitespace. Exactly the opposite of compact.

        As long as you're spreading it out a bit, you probably should also use the if/unless keywords in their non-modifier form. This just makes things so much easier to understand. Remember - we're not writing code for the computer, but for the human reader. ;-)

Re: Complex conditional statements
by monarch (Priest) on Jun 17, 2005 at 07:51 UTC
    Interestingly enough, a good portion of an electrical engineering degree, at least in first and second years, involves boolean logic.

    It is crucial, especially in hardware engineering, to be able to deal with long boolean statements like this.. you can do anything with NOT, AND, and OR. There are a number of transforms you can make when dealing with complex boolean logic problems, and possibly one theorem you may want to check out is: oh forget it, I've just been searching the net for half an hour and the only references to the all-important de moivre's theorem of boolean logic are university course advertisements. You may just have to buy a book!