35

I have a condition

if(exists && !isDirectory || !exists)
{}

how can I modify it, so that it may be more understandable.

  • 1
    what value does isDirectory have when exists is false? – marktani Oct 5 '12 at 12:02
  • 1
    exist is Bool type, isDirectory is also BOOL type variables – Spynet Oct 5 '12 at 12:05
  • 5
    if(!isDirectory) ... (exists || !exists) will always be true. – Shark Oct 5 '12 at 15:29
  • @Shark - What if exists and isDirectory are both true? – pasawaya Oct 6 '12 at 2:13
  • I read the title as "I have a personality problem, can I erase my mind to fix it". Yeah, I'm tired. – Annan Oct 12 '12 at 1:55
110

|| is commutative so

if(!exists || (exists && !isDirectory))

is equivalent.

Now because exists is always true in the second part of the || you can drop the &&:

if(!exists || !isDirectory)

Or you can go a step further and do:

if(!(exists && isDirectory))
  • 5
    What was implied but not explicitly mentioned here is that && has higher precedence (at least in most well known languages - there may be exceptions) than ||. Thus a && b || c is equivalent to (a && b) || c but not to a && (b || c). – Péter Török Oct 5 '12 at 12:21
  • 27
    I think, that !exists || !isDirectory is more "understandable", because, isDirectory cannot be true if !exists. So as a human we will say "if it does not exist or it [exists and it] is not a directory". – duros Oct 5 '12 at 12:39
  • 6
    I prefer !exists || !isDirectory over the last one. – Apoorv Khurasia Oct 5 '12 at 12:40
  • 3
    || is only commutative if used on values without side effects - if for example used with functions some functions might not get called (short-circuiting) or return a different value in a different order. – orlp Oct 5 '12 at 13:36
  • 26
    Anyone who relies on the relative precedence of '&&', '||', '==', '!=', etc and doesn't make their intention clear by using brackets deserves to be shot. In all languages, something like 'a && b || c' is equivalent to a comment saying that the author probably screwed the entire thing up in their rush to avoid typing a few extra characters. – Brendan Oct 5 '12 at 16:26
51

As a process, I suggest building a truth table:

e = exists
d = isDirectory

e | d | (e && !d) || !e
--+---+----------------
0 | 0 | 1
0 | 1 | 1
1 | 0 | 1
1 | 1 | 0

This matches the NAND operation, which is simply:

!(exists && isDirectory)

If you don't remember all your logic gates, wikipedia has a nice reference with the truth tables to boot.


@Christoffer Hammarström brought up an important point about the state of isDirectory being tied to the state of exists. Assuming that they refer to the same reference, and that it's not possible to have a state where the reference doesn't exist and is a directory, the truth table can be written as follows:

e | d | (e && !d) || !e
--+---+----------------
0 | 0 | 1
0 | 1 | n/a
1 | 0 | 1
1 | 1 | 0

The n/a is used to represent a state that doesn't matter. Acceptable reductions could result in either 1 or 0 for states resulting in n/a.

With this in mind, !(exists && isDirectory) is still a valid reduction, resulting in a 1 for !e && d.

However, !isDirectory would be a much simpler reduction, resulting in 0 for !e && d.

  • 4
    The next step is to realise that isDirectory depends on exists. It can't both be a directory and not exist. – Christoffer Hammarström Oct 6 '12 at 0:12
  • @ChristofferHammarstrom, Out of context I can't assume that the variables refer to the same thing, but that is a valid point. The result column should be filled with n/a in places where the state is impossible to achieve, and the equation reduced accordingly. – zzzzBov Oct 6 '12 at 4:24
  • Well, if the variables refer to two different contexts, then they are too terse and need to be renamed. – Christoffer Hammarström Oct 6 '12 at 13:22
  • But building a truth table and evaluating it is NP-complete! – Thomas Eding Oct 17 '12 at 20:18
  • @ThomasEding, I have two quotations for you then, "In theory, theory and practice are the same; in practice, they are not." and "Premature optimization is the root of all evil." – zzzzBov Oct 17 '12 at 20:23
22

For better readability, I like to extract boolean conditions to methods:

if(fileNameUnused())
{...}

public boolean fileNameUnused() {
   return exists && !isDirectory || !exists;
}

Or with a better method name. If you can name this method properly, the reader of your code doesn´t need to figure out what the boolean condition means.

  • +1 for saying something about useful names. But somewhere you will have to reformat the conditional. – Apoorv Khurasia Oct 5 '12 at 12:41
  • 4
    A less extreme alternative, that still conveys intent, is just to name the condition used: boolean fileNameUnused = !exists || !isDirectory; if (fileNameUnused) { doSomething(); } – Steven Oct 6 '12 at 20:00
8

You could just try to nail the no-go case and bail out if that shows up.

while(someCondition) {

    if(exists && isDirectory)
        continue;
        // maybe "break", depends on what you're after.

        // the rest of the code
}

or even

function processFile(someFile)
{ 
    // ...
    if(exists && isDirectory)
       return false;
    // the rest of the code
    // ...
}
  • Aren't break, continue, and more-than-one return statement considered code smells? – Freiheit Oct 5 '12 at 14:37
  • 8
    @Freiheit It depends on the context. Sometimes an early return statement is used to reduce indentation, thus enhancing readability. – marco-fiset Oct 5 '12 at 14:57
  • Best answer - complex conditionals waste enormous amounts of time reading and accurately understanding them. As a result often are "taken as read" leading to insidious bugs. – mattnz Oct 5 '12 at 22:08
6

You can use a truth table as pointed out. Second step could be a KV-map for minimizing the number of terms.

Using the laws of Boolean algebra is another approach:

A = exists
B = !isDirectory
!A = !exists

&& = *
|| = +

[Edit]
A simpler transform, because the operations AND and OR are mutually distributive :

exists && !isDirectory || !exists
= A*B + !A
= (A + !A) * (B + !A)
= 1*(B + !A)
= B + !A
[/Edit]

exists && !isDirectory || !exists
= A*B + !A
= A*B + !A*1 //Identity
= A*B + !A*(B+1) //Annihilator
= A*B + !A*B + !A //Distributivity and Identity
= B*(A + !A) + !A //Distributivity
= B*1 + !A //Complementation 2
= B + !A //Identity
= !isDirectory || !exists

Or with double complement (!!x = x):

A*B + !A
= !!(A*B + !A)
= !(!(A*B)*A)
= !((!A + !B)*A)
= !(!A*A + !B*A)
= !(0 + !B*A)
= !(!B*A)
= B + !A
= !isDirectory || !exists

5

I don't like to use "!" when there is more than one condition in the expression. I'll add lines of code to make it more readable.

doesNotExist = !exists;
isFile = exists && !isDirecotry;
if (isFile || doesNotExist) 
   {}
1

As previously indicated, the condition can be reduced to:

if (!(exists && isDirectory))

However, I'll bet that being a directory implies existence. If so, we can reduce the condition to:

if (!isDirectory)

protected by gnat Sep 18 '15 at 7:34

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.