Is !(p && q) exactly the same as !p || !q?

I’m not sure how clearly it’s been stated so far, but the AND operator doesn’t have to evaluate both arguments either.

With the OR operator: If the first operand is TRUE then the whole expression is TRUE and the second operand can be skipped.

With the AND operator: If the first operand is FALSE then the whole expression is FALSE and the second operand can be skipped.

Furthermore, it’s not necessarily well-defined which operand is the “first” one and which is the “second”. If either or both operands are themselves expressions or function calls, the compiler is free to evaluate either the left or the right operand first.

Yeah, I think learning programming will keep me busy for a while . . . . I’m only on page 83.

AND was mentioned previously. RitterSport seemed to be saying OR required both arguments to be evaluated but I’m not sure what he meant.

Order of evaluation is another variable, languages differ on the importance of evaluation order. So you don’t just need to know if the operation can be resolved with just one of the arguments, you need to know which argument will be evaluated first. Optimizing compilers are pretty sophisticated these days so it’s not the problem it once was, but if you ever have to migrate code from one platform to another these potential differences are a royal pain to deal with. Unfortunately, in so many cases, the original version of the code was written without regard to these factors and may not even be working as believed to begin with.

NO NO NO.

In any language that supports short circuit evaluation, the order of operations must be deterministic. The compiler can’t evaluate (a && b) by evaluating b first. For example, the C standard says:

In general, most programming languages have little quirks like this that can cause big headaches, but good programming practice (like pre-evaluating your functions into variables and sticking those variables into the expression) will generally avoid the headaches so you don’t actually need to learn all of the details.

One other place where pre-evaluation is essential, incidentally, is when working with (pseudo-)random numbers. Suppose, for instance, that your language has a function called rnd() which returns a floating point number uniformly chosen from [0,1). And you want to choose between four possibilities. A naive programmer might write something like


if(rnd() < 0.25) then do A;
else if(rnd() < 0.5) then do B;
else if(rnd() < 0.75) then do C;
else do D;

But this won’t work right, because each rnd() will be called separately. You’ll end up with A happening 1/4 of the time, B happening 3/8 of the time, C happening 9/32 of the time, and D happening 3/32 of the time (but since all of them will sometimes happen, it’ll drive you nuts trying to figure out why the probabilities are wrong, if you even notice). Instead, you need to say


r = rnd();
if(r < 0.25) then do A;
else if(r < 0.5) then do B;
else if(r < 0.75) then do C;
else do D;

which guarantees that it’s the same random number you’re looking at each time.

EDIT:

Or rather, it can, as long as it always does that, and it’s in the documentation that it always does that.
Or, to nitpick yet further, you can have a programming language that’s inconsistent about it, or that is consistent but doesn’t document which way it does it, but that programming language is a bad one.

I don’t know if he was specifically referring to C. I was not. Because of these kinds of problems and the popularity and dependency on C you should be able to trust that rule. This whole area is not the problem it once was, today’s problems are more commonly about things like library code differences, but it’s important for programmers to understand these problems and why their code may not be working as they expected.

The whole point of short circuit evaluation is the programmer can rely on the order of evaluation, so you can write code like



if (p != NULL && p->x == y) ...


and not crash because p->x was evaluated first. A language that allows the second clause of && to be evaluated first should not claim to support short circuit evaluation.

I would say the whole point of ‘short-circuit’ evaluation is efficiency of execution. Knowing the order of execution is necessary as a result of that. Of course some would feel it is a feature to reduce the volume of code through clever expressions but I just see that technique as planting seeds for future problems. YMMV

Ah, this is extremely enlightening to me - it really demonstrates the benefit or storing values into variables. The book mentions stuff like this as “good practice” in passing, but seeing it demonstrated this way gives me a different level of understanding.

The !(p && q) doesn’t scream “don’t proceed unless I have both p and q”; it means “don’t proceed IF I have p and q”.

“i” is an imaginary number. How is it not a number?

^:dubious:^ Ah, imprecision. It is a difficult concept.

You want to want to bring animals back from the dead? :dubious:

What you were doing with your “pole” is a little TMI. This and that “seminal” work you mentioned make me wonder about what you are planning on doing with these animals!

The imprecision of English when it comes to logic:
Captain Kirk: Everything Harry tells you is a lie. Remember that. Everything Harry tells you is a lie.
Harcourt Fenton Mudd: Now listen to this carefully, Norman. I am… lying.

It is very, very, very, very, very, very, very standard in C and C++ and their derivatives to use short circuiting as a protection against evaluating the second operation. It’s considerably less “planting seeds for future problems” and much more “good style”.

And I gotta say, any programming language that short-circuited boolean operations right-to-left would be the insane gibberings of a madman. Such a thing would be incredibly unintuitive.

Moe: “Mind your p’s and q’s.”

Curly: “Don’t forget to dot the i’s.”

Moe: “Certainly.”

  • pokes Curly in the eyes.

Heck, there are programming languages that evaluate everything right-to-left. Though I won’t actually say that those languages aren’t insane gibberings.

And the main reason I wouldn’t call it good style to deliberately use short-circuiting is that not all languages use short-circuiting, and so someone who’s used to writing for one that does might end up falling into a trap if he ever finds himself using one that doesn’t. Likewise, someone who’s used to a language that does not, but finds himself reading code in a language that does, might misinterpret it. I put a high priority on portability in good practice.

Another example of good practice is in order of operations. If you’re not sure if an expression will evaluate in the order you intend, you can go look up the order of operation rules for your language… Or you can just put in parentheses. And if it’d take too many layers of nested parentheses to make an expression clear in that way, then that’s about the time where you’d probably want to make auxiliary variables, for the sake of human readability.

Speaking as a working programmer, I don’t think that’s a going concern because languages that are that different look that different, and can’t be casually read without bothering to actually learn what the language is. And attempting to write your actual code as generic pseudocode will lock you out of so many language features that you’ll have effectively hamstrung yourself - if you’re eschewing short-circuiting in C because Pascal doesn’t reliably have it, you should also be eschewing For loops because the C style is utterly incomprehensible if you only know Pascal.

I’ve found that piles of parentheses reduce readability significantly, and often use various other tricks to make things clear. As you say, after correctness, readability is paramount - though you are allowed to assume your reader knows the language.

Given the thread title, you don’t know how close I came to zapping this as spam.:wink:

I’ve been a professional programmer for almost 40 years, and I’ve seen good code, bad code and everything in between, but I’ve never seen any professional programmer avoid short circuit evaluation just because there are some languages that don’t support it. That would be absurd. As begbert2 says, it is a completely standard style which everyone uses all the time. We don’t avoid using arithmetic expressions in C because assembly language doesn’t have expressions. We don’t avoid using classes in C++ because C doesn’t have classes. We don’t ignore useful language features just because someone ignorant of the language might someday read our code.

And I totally agree with begbert2 that extraneous parentheses reduces readability. It drives me nuts to see code like



if ((a == (b+1)) && ((c == (b+2)) && (d == (c+3)))


when only the outer parentheses are necessary. This reduces readability. And adding unnecessary temporary variables reduces readability even more.

Congratulations, in a few years you’ll have as much experience as I do now. Or maybe condolences is more apt :wink:

The reason to avoid ‘short-circuit’ evaluation isn’t because some other language may not have the feature or do it differently. There’s no reason to avoid it at all, but there is plenty of reason to avoid using it inappropriately. The example used is quite simple and easily recognizable to C programmers, but code is always that simple, and may not stay that way. Code gets changed over time, often by a chain of increasingly less experienced and knowledgeable programmers. I’ve seen problems from such simple expressions after someone used a search and replace to change all occurrences of a variable name with a function. And I’ve seen programmers royally mess up code by not understanding how it works, sometimes just using a snippet and trying to modify to their purpose which may be quite different than what it was intended for.

Since my work often involves solving the problems caused by poor practices I may be a bit stodgy on these points but after spending far more time fixing the problems created by others than in my own code I’ll stick with the way I do it.

We agree there. Operator precedence does change among languages, but even then it’s best to use minimal parentheses for clarity. White space works just as well. I usually work in a studio that highlights matching parentheses, but even then it’s still annoying to see totally superfluous syntax that detracts from readability.

I generally try to eschew dependence on short-circuiting except for idiomatic constructs. For example:


if (!p || !p->bloop())

Clearly, this depends on short-circuiting so there’s no null pointer dereference on bloop().

I also use it for performance reasons, such as:


if (fastCheckWithFalsePositives() && slowPerfectCheck())

However, if the logic requiring the short-circuit is buried somehow, I prefer to make things more obvious. For example, I wouldn’t write this:


if (foo() && bar()) // foo() *must* execute before bar()

I’d write this instead:


if (foo()) { /* only valid when foo() is true */ if (bar()) { } } 

Of course, these are just preferences and judgment calls, and subject to revision. I agree that no one should avoid them because other languages don’t support it.

PS: Anyone else having problems with multi-line CODE blocks? Normally I’d have a few newlines above, but the new board layout isn’t allowing it.

Ada gives you an explicit choice between “and”, “and then”, “or”, “or else”. It even reserves the right to evaluate the clauses in parallel on a normal “and” or “or”. (Note: I, like most people, have never used Ada.)

Visual basic does too. It’s because modern visual basic is a direct descendent of old visual basic with predated the idea of short-circuiting, and so they added the AndAlso and OrElse operators to modernize the language without breaking legacy code.