Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot

(Ovid - Why I love nested If-Else blocks)

by Ovid (Cardinal)
on Jan 04, 2002 at 02:28 UTC ( #136122=note: print w/replies, xml ) Need Help??

in reply to Why I Hate Nested If-Else blocks

Take a look at my CSV Database Validation program. I use a few nested If-Else-If blocks and I consider them to be very useful. If the next if's are logically dependant on one another, they are a Good Thing. As for the example you provide, I think you could have done better (sorry!). Most (not all) usage of the ref function is due to poor design on the part of the programmer and examining the code can give ideas on how to redesign to avoid that. Ignoring that for a moment, let's look at this code:

if ( $foo > 7 ) { if ( $bar ) { bar_func( $foo ); } elsif ( $baz ) { baz_func( $foo ); } }

The two subroutines are dependant on $foo evaluating as true. If I want to avoid nested ifs, I could do this:

if ( $foo > 7 and $bar ) { bar_func( $foo ); } elsif ( $foo > 7 and $baz ) { baz_func( $foo ); }

Frankly, while that works, it's poor programming style. You have duplicate code and that's not a good thing. If the two functions are logically dependant on $foo's value, they should be grouped so that you only test the value once. If those sections of code are far apart, it's easy to miss updating one of the tests, if the condition needs to change. Sure, I could create dispatch tables or jump through all sorts of hoops to get around this, but nested if statements can add clarity to code (unless you're nesting eight levels deep).


Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Replies are listed 'Best First'.
(jeffa) 2Re: (Ovid - Why I love nested If-Else blocks)
by jeffa (Bishop) on Jan 04, 2002 at 03:05 UTC
    You have a point and i do agree, but what happens when you scale your example up? That's what i am talking about here. Consider this:
    if ( $foo > 7 ) { if ( $bar ) { bar_func( $foo ); } elsif ( $baz ) { baz_func( $foo ); } elsif ( $qux ) { baz_func( $foo ); } } elsif ( $foo <= 7 ) { if ( $bar ) { other_bar_func(); } elsif ( $baz ) { other_baz_func(); } elsif ( $qux ) { other_qux_func(); } }
    Get's hairy quick. At that point, i would definetaly try a dispatch table with sub references. Now, to _really_ go out on a limb, how about a goto as a replacement for your first example, since you are only concerned with calling the subs if one condition is true:
    goto SKIP unless $foo > 7; if ($bar) { bar_func($foo); } elsif ($baz) { baz_func($foo); } SKIP: # more code
    Notice i didn't get rid of the outer if, i just seperated it from the inner if. Even though i use a goto, it looks more readable to me, simply because the indention level. Well, it looks more readable as long as i don't have to scan more than 10 lines to find where the goto is going! Besides, if this were inside a sub, i could replace the goto with return.

    BUT ... I do agree that your first example is more readable than your second, and is much better form than my goto solution.

    Did i just use a goto?!?


    (the triplet paradiddle)
      At that point, i would definetaly try a dispatch table with sub references

      I might do something like this:

      my $func = ($foo > 7) ? $bar && \&bar_mysub || $baz && \&baz_mysub : $bar && \&bar_other_mysub || $baz && \&baz_other_mysub; $func->($foo) if $func;
      Update: I admit I might reformat it slightly, but except for the '?:' operator, its not any different than the last example in perlsyn under "Basic BLOCKs and Switch Statements". (We do want to encourage newbies to read the FAQs, right? :-)

      Update: For the curious, here's what perltidy does with a longer version of the above (the above code puts makes perltidy put more on one line, so I added a bit more so it would get more broken up):

      my $func = ( $foo > 7 ) ? $bar && \&bar_mysub || $baz && \&baz_mysub || $bam && \&bam_mysub || $bak && \&bak_mysub : $bar && \&bar_other_mysub || $baz && \&baz_other_mysub || $bam && \&bam_other_mysub || $bak && \&bak_other_mysub; $func->($foo) if $func;
      As always, decide for yourself if this is better or worse, more or less readable, or more or less readable to someone else, than whatever else you might come up with (I actually like jeffa's version below better) :-)
        I might do something like this:
        my $func = ($foo > 7) ? $bar && \&bar_mysub || $baz && \&baz_mysub : $bar && \&bar_other_mysub || $baz && \&baz_other_mysub; $func->($foo) if $func;
        Don't align things vertically for the sake of aligning them, align them so that they're readable! The fragment above borders on obfuscation. There's a mind-numbing repetitive pattern on the right, and this
        || : ||
        thing on the left. Yuch. I had to stop and really look at that fragment to get past the visual clutter.

      It's worth noting that your elsif could easily be replaced by a simple else ;-).

      I would probably end up opting for:

      $bar && $foo > 6 ? bar_func($foo) : other_bar_func(); $baz && $foo > 6 ? baz_func($foo) : other_baz_func(); $quux && $foo > 6 ? quux_func($foo) : other_quux_func();
      Of course if you've got code in the block and not just subroutine calls that might get a little ugly. But it's dense (I like dense) and (to me) straight forward.

      UPDATE: Yes, I got the condition wrong (that's what I get for thinking in integers ;-). Quoth a maven, "it's a dispatch table without a hash!"

      UPDATE2: Umm it seems the "?" walked off, they are back.

      perl -pe "s/\b;([st])/'\1/mg"

      Did i just use a goto?!?

      Well, if you think using a goto is more readable than using another construct, then use it. I have seen code that were emulating goto's, and which were worse, much worse than the same code written with goto's.

      I have read somewhere that there was Dijkstra's paper goto considered harmful, ( and then somebody answered with another paper "goto considered harmful" considered harmful. Unfortunately, I have not been able to find it.

      I don't see anything that improved on the nested if/else blocks so far. You have 6 cases so you are going to end up with six cases. You could factor out the common code like so:

      { no strict 'refs'; &{ ( $foo < 7 ? "" : "other_" ) . ( $bar ? "bar" : $baz ? "baz" : $qux ? "qux" : last ) . "_func" }( $foo ); }
      but that is pretty obfuscated. (:

              - tye (but my friends call me "Tye")
Re: (Ovid - Why I love nested If-Else blocks)
by runrig (Abbot) on Jan 04, 2002 at 02:53 UTC
    You can debate all day about whether this is good or bad, better or worse (as we seem to be doing on that other thread), but I'm compelled to mention it (-:
    { last unless $foo > 7; bar_func($foo),last if $bar; # Or... $bar and do { bar_func($foo); last }; baz_func($foo), last if $baz; }

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://136122]
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others pondering the Monastery: (5)
As of 2018-06-22 05:51 GMT
Find Nodes?
    Voting Booth?
    Should cpanminus be part of the standard Perl release?

    Results (121 votes). Check out past polls.