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

Hi Monks!!!

Ok, this might seem like a noob question but I'm confused on Perl's behaviour. I have code that checks 2 flags, whether they are "true" or not. Until both of them are set to "true", my script is required to sleep and wait for them. Here's my code:

print "Waiting for all workers to finish their tasks ..."; while (($worker1Finished ne "true") && ($worker2Finished ne "true")) { print "."; sleep(1); }

Well, as you punters might have guessed, I'm having problems with the way Perl "short circuits" my logical AND (&&) operator in the while condition.

According to perldoc.perlop, Binary "&&" performs a short-circuit logical AND operation. That is, if the left operand is false, the right operand is not even evaluated. Scalar or list context propagates down to the right operand if it is evaluated. So, in my code, if worker 1 has not yet finished, the script will wake up before it is supposed to...and this is resulting in hara-kiri!

The functionality of my code is such that either worker1 can finish first, or worker 2... there is no "predefined" order in which these flags are set to "true". How do I ensure that both flags are checked for "true" and only after this, the execution moves forward ???

Replies are listed 'Best First'.
Re: Short circuits in Logical AND (&&)
by davido (Cardinal) on Aug 02, 2011 at 06:06 UTC

    There's a logic error. You want to idle until both A and B are true. But the way you have it constructed, if A is true, then it will stop executing. Or if B is true, it will stop. The only way it can keep going is if both are not true.

    It's difficult to spot because you're testing for the Boolean truth of a negative. What you're saying is "While A is untrue and B is untrue they both are untrue, so keep looping." But that overlooks if A is true and B is not. If A is true, then it realizes the first term is equal to true which gives you a boolean false in the first term, which collapses your 'and'. If either one of the terms evaluates to Boolean false, the 'and' cannot be true, so the while loop terminates.

    You probably want:

    while( $worker1Finished ne 'true' or $worker2Finished ne 'true' ) { #... }

    In other words: "While A is untrue or B is untrue then both A and B cannot be true, so keep looping."

    In that case, as long as either one of them is not 'true', you continue. The only way to stop is if both of them are not true. It could be much easier to wrap your mind around the logic (at least it is for me) if you simplify it a little by using an until() block. I rarely use them in the until(){} form, but this seems like an ideal situation; especially since you said right at the top of your post "I want to do something until..." (paraphrasing):

    until( $worker1Finished eq 'true' and $worker2Finished eq 'true' ) { print "."; sleep 1; }

    Read that as "Until A is true and B is true, keep looping."


    Dave

      Oh!! I generally dont like to use the "until" block .. but as you said, this may be a situation that's ideal for its use. If in case I'd want to write this with a while block, would this logic given below work?

      while(!(($worker1Finished eq "true")&&($worker2Finished eq "true"))) { print "."; sleep(1); }

      Am just trying to learn the different cases for using the "while" and "until" constructs ... So, please give me your suggestions/advice.

        Yes, there are a lot of ways to write it correctly, and a lot of incorrect ways too. ;) The one you proposed should work fine. But I would use the lower precedence operators:

        while( not ( $worker1Finished eq 'true' and $worker2Finished eq 'true' ) ) { # ... }

        ...to at least eliminate one set of parens. In fact, if my memory of the precedence tables serves me (which it may not), you could eliminate the other set of parens like this:

        while( not $worker1Finished eq 'true' && $worker2Finished eq 'true' ) { # ... }

        Because 'eq' is higher precedence than &&, and && is higher precedence than 'not', so eq binds first, && second, and 'not' last.

        But the gods gave us 'until(){}' because it suited them to do so. While it's important to use caution when wielding negatives (and double negatives, etc.), it is useful sometimes. My preference in this case is to use it.

        until( $worker1Finished eq 'true' and $worker2Finished eq 'true' ) { # ... }

        Personal preference. But to me "until this and that keep looping" reads better than "while not both this and that keep looping."


        Dave

Re: Short circuits in Logical AND (&&)
by kcott (Archbishop) on Aug 02, 2011 at 06:47 UTC

    If your true and false flags equate to one and zero, you can write your loop like this:

    until ($worker1Finished & $worker2Finished) { ... }

    Update: s/zero and one/one and zero/

    -- Ken

      however, for the cost of one extra & character (I can supply them very cheaply if you are running out) you can write:

      until ($worker1Finished && $worker2Finished) { ... }

      which will work nicely for all Perl's various true and false values (including not complaining about undef values under strictures).

      True laziness is hard work

      Uh uh ... is that construction short-circuit?   Not sure that it is.   And if not, “not equivalent.”

      Also, I do not like to write code that “assumes” what “just happens to be the case right now,” in such a way that it requires it and therefore breaks down when something causally unrelated to it (i.e. the assumption...) changes in what ought to be an inconsequential way.   That is the sort of coding that bites you in the punctuation-mark.   I’ve been bitten by it enough times, and have cleaned up after it enough times, to have come to really resent it.

        Uh uh ... is that construction short-circuit? Not sure that it is. And if not, “not equivalent.”
        Rubbish.

        Short-circuiting and not short-circuiting are only non-equivalent if the second expression has side-effects. Since it's not mentioned the variables are tied, it's safe to assume there's not short-circuiting happening.

        So, under the stated assumptions (using 0 and 1), they are equivalent.

        Not that I would use a construct. I find using bit-twiddling operators to do boolean logic a misplaced cuteness that serves nothing. To me, it smells like the author is saying "look at me, I think I've surpassed the level of grasshopper".