37

This is related to a chapter from beautiful code. And in that chapter I read about the nested ifs.

The author was talking about deeply nested ifs as originator of bugs and less readable. And he was talking about replacing nested ifs with case statements and decision tables.

Can anybody illustrate how to remove nested ifs with case (select case) and decision tables ?

Biswanath
  • 9,075
  • 12
  • 44
  • 58

11 Answers11

30

Well, not directly an answer to your question since you specifically ask about switch/case statements, but here is a similar question.

Invert “if” statement to reduce nesting

This talks about replacing nested if's with guard-statements, that return early, instead of progressively checking more and more things before settling on a return value.

Community
  • 1
  • 1
Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
14

One example I always try to do is replace heavily nested if's like this (actually this one's not too bad but I've seen them up to 8 or 9 levels deep in the wild):

if (i == 1) {
    // action 1
} else {
    if (i == 2) {
        // action 2
    } else {
        if (i == 3) {
            // action 3
        } else {
            // action 4
        }
    }
}

with this:

switch (i) {
    case 1:
        // action 1
        break;
    case 2:
        // action 2
        break;
    case 3:
        // action 3
        break;
    default:
        // action 4
        break;
}

I also try to keep the actions as small as possible (function calls are best for this) to keep the switch statement compressed (so you don't have to go four pages ahead to see the end of it).

Decision tables, I believe, are simply setting flags indicating what actions have to be taken later on. The "later on" section is simple sequencing of actions based on those flags. I could be wrong (it won't be the first or last time :-).

An example would be (the flag-setting phase can be complicated if's since its actions are very simple):

switch (i) {
    case 1:
        outmsg = "no paper";
        genmsg = true;
        mailmsg = true;
        phonemsg = false;
        break;
    case 2:
        outmsg = "no ink";
        genmsg = true;
        mailmsg = true;
        phonemsg = false;
        break;
    default:
        outmsg = "unknown problem";
        genmsg = true;
        mailmsg = true;
        phonemsg = true;
        break;
}

if (genmsg)
    // Send message to screen.
if (mailmsg)
    // Send message to operators email address.
if (phonemsg)
    // Hassle operators mobile phone.
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 3
    Because "else if" is evil, and must be written as "else { if ... }"? But you're right to use a switch for simple ifs like this. – JeeBee Dec 03 '08 at 13:43
9

How about chained ifs?

Replace

if (condition1)
{
    do1
}   
else
{
    if (condition2)
    {
        do2
    }
    else (condition3)
    {
        do3;

    }
}

with

if (condition1) {
   do1;
} else if (condition2) {
   do2;
} else if (condition3) {
   do3;
}

This is much like switch statement for complex conditions.

8

Make the condition into booleans and then write boolean expression for each case.

If the code was:

if (condition1)
{
    do1
}   
else
{
    if (condition2)
    {
        do2
    }
    else (condition3)
    {
        do3;

    }
}

One can write it as:

bool cond1=condition1;
bool cond2=condition2;
bool cond3=condition3;

if (cond1) {do1;}
if (!cond1 and cond2) {do2;}
if (!cond1 and cond3) {do2;}
khivi
  • 81
  • 4
  • 2
    This gets really ugly when there's lots of conditions. Plus, readability is lost because the actual conditions are hidden behind the boolean values. – Daniel Becroft Sep 24 '12 at 20:26
  • @DanielBecroft I actually see it the opposite way. I agree with [Jeff Atwood's approach of avoiding comments](http://blog.codinghorror.com/coding-without-comments/), and to do that, you give that boolean variable a meaningful name, and that will *enhance* readability... With that said, this approach presented here is not a silver bullet, though. What if you don't want to unnecessarily compute the conditions? – Mazyod Jul 06 '14 at 08:19
  • Agreed with @Mazyod The downside of above was the conditions get unnecessarily computed. In Scala a lazy val helps there. – khivi Mar 09 '16 at 16:06
3

For decision tables, please see my answer to this question, or better still read chapter 18 in Code Complete 2.

Community
  • 1
  • 1
Yuval F
  • 20,565
  • 5
  • 44
  • 69
2

You can just break once a part of the validation failed for example.

function validate(){
  if(b=="" || b==null){
      alert("Please enter your city");
      return false;
  }

  if(a=="" || a==null){
      alert("Please enter your address");
      return false;
  }
  return true;
}
Alexis Paques
  • 1,885
  • 15
  • 29
1

Decision tables are where you store the conditional logic in a data structure rather than within the code itself.

So instead of this (using @Pax's example):

if (i == 1) {
    // action 1
} else {
    if (i == 2) {
        // action 2
    } else {
        if (i == 3) {
            // action 3
        } else {
            // action 4
        }
    }
}

you do something like this:

void action1()
{
    // action 1
}

void action2()
{
    // action 2
}

void action3()
{
    // action 3
}

void action4()
{
    // action 4
}

#define NUM_ACTIONS 4

// Create array of function pointers for each allowed value of i
void (*actions[NUM_ACTIONS])() = { NULL, action1, action2, action3 }

// And now in the body of a function somewhere...
if ((i < NUM_ACTIONS) && actions[i])
    actions[i]();
else
    action4();

If the possibilities for i are not low-numbered integers then you could create a lookup table instead of directly accessing the ith element of the actions array.

This technique becomes much more useful than nested ifs or switch statements when you have a decision over dozens of possible values.

Paul Stephenson
  • 67,682
  • 9
  • 49
  • 51
0

Nested if are equivalent to the logical operator AND

if (condition1)
{
    if (function(2))
    {
        if (condition3)
        {
            // do something
        }
    }
}

Equivalent code:

if (condition1 && function(2) && condition3)
{
    // do something
}

In both cases, when an expression evaluates false, the subsequent expression will not be evaluated. For example, if condition1 is false, the function() will not be called, and condition3 won't be evaluated.

Demis Palma ツ
  • 7,669
  • 1
  • 23
  • 28
  • 3
    It is not equivalend :) because `function(2)` will not run if `condition1` is false, in the second example all 3 functions will run, which can affect performance. – rossanmol Dec 30 '16 at 14:02
  • No, @PHPLover - PHP uses lazy evaluation (sometimes called short-circuit evaluation), so if the first condition in a logical AND is false, it won't attempt to evaluate any of the other conditions. Likewise, if you were doing an OR and the first condition was true it wouldn't evaluate the second. – Demis Palma ツ Dec 31 '16 at 16:16
  • they are not equivalent still, I've just tested, if the second argument has an error, an error will be displayed which means that the if statement still runs all arguments. – rossanmol Jan 08 '17 at 15:48
  • @PHPLover I can't imagine what kind of code you tested and how you conducted the test. Lazy evaluation is an old time, well known feature common to all major programming languages, that programmers must take into account when writing their code. You can try the PHP code online at http://sandbox.onlinephpfunctions.com/code/19e6e86d7ccb562325c8b8f4eea1e9539a5006fd Nested IF and logical AND operation behave the same. Can you provide your code snippet to prove the contrary? – Demis Palma ツ Feb 21 '17 at 15:28
0

If and switch statements are not purely OO. They are conditional procedural logic, but do a very good job! If you want to remove these statements for a more OO approach, combine the 'State' and 'Descriptor' patterns.

Anthony Mastrean
  • 21,850
  • 21
  • 110
  • 188
0

You might also consider using the Visitor pattern.

Drejc
  • 14,196
  • 16
  • 71
  • 106
-1

Another example some languages allow is this

           switch true{
            case i==0
              //action
            break

            case j==2
             //action
            break

            case i>j
             //action
            break
           }
Jim C
  • 4,981
  • 21
  • 25