http://qs321.pair.com?node_id=136097

This is not a rant, but instead mantra. Nested if-else statements should be avoided!

Impossible you say? It is true that not all problems can avoid a mess of nested if-else blocks, but, with the help of Perl's syntax and use of hashes as lookup (or dispatch) tables, you might be suprised at how much your code could be cleaned up.

<update> Ovid made me realize that another important decision to make about using nested if-else blocks or avoiding them is to determine which style produces the least redundant code. </update>

Caveat - don't get caught up in Premature optimization - meeting all the specification requirements is much more important. But then again, somebody is going to have to maintain it. If you are interested in job security through obscurity then this node is not for you. ;)

Consider this object method that accepts either a scalar argument or an array reference (of course, most other languages require you provide two seperate methods):

sub foo { my ($self,$ref) = @_; if (ref $ref eq 'ARRAY') { if ($#$ref) { # do case number 1 } else { # do case number 2 } } else { # do case number 2 } }
I have to determine if the user passed an array reference or a scalar no matter what. But, in this case i also want to know how many elements were passed in an array reference, because if they pass only one element, then that really should be the same as passing a scalar. (Also note that i am not checking to make sure $ref is not undef or is a another kind of reference - keepin it simple.)

The problem is that i can't just test for $#$ref, because that will cause a syntax error under strict if $ref is a scalar. So, i have to use another if-else block inside the block for the case of an array reference. Yuck. And on top of that, i am repeating a whole code block. Super yuck.

However, if i make sure that $ref is indeed an array reference even if it isn't, then i can condense this down to two blocks:

sub foo { my ($self,$ref) = @_; $ref = [$ref] unless ref $ref eq 'ARRAY'; if ($#$ref) { # do case number 1 } else { # do case number 2 } }
Now, there is more than one way to condense the original problem, this is just one. I am not necessarily looking for other ways to solve this problem.

The reason i post this meditation is because i am interested in what others have done to condense and refactor if-elsif-else blocks for simplicity. And using Switch doesn't count! ;) But using dispatch tables definitely does.

P.S. In school i studied Karnough maps which are used to simplify circuit design. Turns out that conditionals (if (this or that and not the other) {}) are not at all different and K-Maps can be used to simply them. Very neat stuff.

jeffa

L-LL-L--L-LL-L--L-LL-L--
-R--R-RR-R--R-RR-R--R-RR
F--F--F--F--F--F--F--F--
(the triplet paradiddle)

Replies are listed 'Best First'.
Re: Why I Hate Nested If-Else blocks
by dws (Chancellor) on Jan 04, 2002 at 02:33 UTC
    Another common way to reduce nesting (and, in some cases, to eliminated nested if-else blocks) is to use guard clauses.

    A guard clause prevents complete entry to a subroutine unless a condition is met. It is a special case of an if-else block, and (if you use them) an exception to the "single entry, single exit" rule imposed by structured programming.

    A guard clause allows:

    if ( ! ref $_[0] ) { return undef; } else { blah($_[0]); }
    to be written as
    return undef unless ref $_[0]; # guard clause blah($_[0]);
    You see guard clauses all the time if you spend any time inside of standard modules like CGI.pm.

    One of my standard refactorings for deeply nested code written by unrepentant structured programming purists is to reduce nesting by "pulling up" the guard clauses.

    Update: I checked Martin Fowler's book Refactoring, and sure enough, he has a refactoring called "Replace Nested Conditional with Guard Clauses." He cites Kent Beck's 1997 book Smalltalk Best Practice Patterns, though Beck was codifying what was already a standard practice in the Smalltalk community.

      Iīm glad you mentioned guard clauses, dws. They are particularly appropriate in light of jeffa mentioning Karnough maps.

      Much of the point of guard clauses (and more elaborate if-then-else structures) is to reduce to a known state at any given moment, i.e., normalize your input states.

      Interestingly, in EE, where Karnough maps are drilled into your head during beginning digital design courses, this takes on added importance when you implement your logic using hardware circuits. The logical reductions provided by Karnough maps do not come for free in the hardware world: they result is so called "donīt care" states, areas on the K map that represent theoretically irrelevant input states that under normal circumstances should never be reached.

      In hardware, however, there is no absolute guarantee as to what state the circuit will land in once you flip on the power. Or, at least, thereīs no guarantee in a poorly designed circuit. You have to guard against these unknown initial conditions with some additional circuitry up front that will guarantee a known starting state.

      None of this, naturally, does diddly squat for your TTL project if you dump a coke on it.

      Matt

      Beware the Santa Clauses!.

(Ovid - Why I love nested If-Else blocks)
by Ovid (Cardinal) on Jan 04, 2002 at 02:28 UTC

    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).

    Cheers,
    Ovid

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

      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?!?

      jeffa

      L-LL-L--L-LL-L--L-LL-L--
      -R--R-RR-R--R-RR-R--R-RR
      F--F--F--F--F--F--F--F--
      (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) :-)
        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, (http://www.acm.org/classics/oct95/) 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")
      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; }
Re: Why I Hate Nested If-Else blocks
by dragonchild (Archbishop) on Jan 04, 2002 at 02:06 UTC
    Bravo! ++.

    This sounds very much like a call to think through one's designs first. (You do design, right?) Laying out the branches on paper is a very good way of doing this.

    Of course, there are some gotchas. For example, the place I'm at right now has a lot of legacy code that uses the form

    sub foo { my $self = shift; if (ref $self) { # Do stuff return $some_useful_value; } else { # Complain return undef; } }
    This is vs. the (to me) more intuitive
    sub foo { my $self = shift; unless (ref $self) { # Complain return undef } # Do stuff return $some_useful_value }
    *blinks* I guess I just proved your point, huh? :-)

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

Re: Why I Hate Nested If-Else blocks
by mstone (Deacon) on Jan 04, 2002 at 02:30 UTC

    I use two general solutions, depending on how complex the problem is.

    The first option is to use a table of function pointers:

    %FLUT = ( 'condition_1' => \&func_1, 'condition_2' => \&func_2, 'condition_3' => \&func_3, ); $condition = ## reduce the input to a known condition $FLUT{$condition}->(@args);

    and since I happen to like object syntax, I have a simple little class that wraps function pointers up as objects:

    package Command; sub new { my $type = shift; my $O = shift; bless $O, $type; return ($O); } sub execute { my $O = shift; return ($O->( @_ )); }

    Naturally, it helps to make sure all the functions/objects in a table take the same list of arguments. That restriction appeals to me, because it helps me write code that ends in a known state no matter what. Nested if-else statements make it a little too easy to set one set of globals in one branch, and another set of globals in another, IMO. That makes it really difficult to decide what state the program is in when it leaves the outermost statement.

    If a project involves serious branching, I'll occasionally roll my own linear state machine:

    package Machine; ## new (nil) : Machine_ref # # trivial constructor # sub new { my $self = bless {}, shift; return ($self); } ## execute # # run through the list of commands, halting when one of them fails # sub execute { my $self = shift; my $model = shift; for $cmd (@{ $self->_get_commands() }) { # [1] last if ( 0 == $cmd->execute ($model) ); # [2] } return(); } ### # [1] iterate over the list of Command objects that belongs to the # specific Machine, executing each one in turn. # [2] drop out of the loop if any Command in the sequence returns # a zero. # # in practice, this is equivalent to executing a set of # short-circuit conditionals: # # if (<condition 1>) { return ('value 1') } # if (<condition 2>) { return ('value 2') } # if (<condition 3>) { return ('value 3') } # # but it's cleaner, and you can make each Command much more complex # than the body of a sensible conditional. ### ## STATE-SPECIFIC OPERATIONS ## _get_commands (nil) : listref # # each Machine has its own list of Commands that should be # executed in response to the user's input. this routine returns # a reference to that list. # sub _get_commands { return ([]); } 1;

    This code comes from a larger project, where the parameter $model was itself an object. The Model was responsible for all the data in the program, and the Commands simply called sets of Model methods.

      Can you please explain the following to me?
      ## _get_commands (nil) : listref # # each Machine has its own list of Commands that should be # executed in response to the user's input. this routine returns # a reference to that list. #
      What prompted you to do that? What I expect is the following?
      =head1 FUNCTIONS .... =head2 C<_get_commands (nil) : listref> each Machine has its own list of Commands that should be executed in response to the user's input. this routine returns a reference to that list. =cut
      If you're going to bother writing more than two lines of documentation, please make it POD, that is what it is for.
       

      ____________________________________________________
      ** The Third rule of perl club is a statement of fact: pod is sexy.

      Bravo! Another proponent of data-driven dispatch.

      "... in practice, this is equivalent to executing a set of short-circuit conditionals ... but it's cleaner, and you can make each Command much more complex than the body of a sensible conditional."

      This approach has the tendency to scare off some programmers, since the state machine engine's code is completely independent from the data which defines the state-machine, but once you have your hands around the concept, the ease with which fundamental changes to the logic may be made justifies the learning curve.

      If one is not careful with this approach, it is easy to end up with Write-Only code, however.

      dmm

      You can give a man a fish and feed him for a day ...
      Or, you can
      teach him to fish and feed him for a lifetime
Re: Why I Hate Nested If-Else blocks
by ignatz (Vicar) on Jan 04, 2002 at 20:49 UTC
    I must confess that I would much prefer a nested if-else block over something like $ref = $ref unless ref $ref eq 'ARRAY'; I don't see much point in jumping through hoops to avoid nesting. Maybe it's just the novice in me talking.

    Yes, there are times when there are much more elegant solutions then nested if-elses, but shouldn't the goal be simplicity over a blanket condemnation? I guess I'm just not seeing the why in "Why I Hate Nested If-Else blocks."

    My personal approach is to try to keep any nested blocks deep in the guts of my code. If I see to much of it, that sends out an alarm that I may need to abstract out things a bit more.


      > Yes, there are times when there are much more elegant solutions
      > then nested if-elses, but shouldn't the goal be simplicity over
      > a blanket condemnation? I guess I'm just not seeing the why in
      > "Why I Hate Nested If-Else blocks."

      A series of nested conditionals forms a state machine. Every block is its own state, and every test marks a transition from one state to the next:

      if ($a) { if ($b) { &S1; } else { &S2; } } else { if ($c) { &S3; } else { &S4; } }
      is equivalent to:
      +-----(b)->[S1]--+ | | +-----(a)->[test b]--+ |--+ | | | | | +-(not-b)->[S2]--+ | [test a]--| |-->[end] | +-----(c)->[S3]--+ | | | | | +-(not-a)->[test c]--+ |--+ | | +-(not-c)->[S4]--+

      Nested conditionals work well if the machine you're building graphs out to a simply-connected binary tree, like the one above, but they become less desirable as your machine grows more complex.

      Basically, the problem boils down to representing arbitrary data structures with a binary tree. There are some things you just can't do directly, like forming complex connections:

      +-----(b)->[S1]-+ | | +-----(a)->[test b]-+ +---->[S5]-+ | | | | | +-(not-b)->[S2]---+ | [test a]-+ | | +->[end] | | | | | +-----(c)->[S3]-+ | | | | +-->[S6]-+ +-(not-a)->[test c]-+ | | | +-(not-c)->[S4]---+

      So you have to simulate the connections by duplicating nodes:

      +-----(b)->[S1]->[S5]-+ | | +-----(a)->[test b]--+ |--+ | | | | | +-(not-b)->[S2]->[S6]-+ | [test a]--| |-->[end] | +-----(c)->[S3]->[S5]-+ | | | | | +-(not-a)->[test c]--+ |--+ | | +-(not-c)->[S4]->[S6]-+

      Which introduces synchronization problems.

      The more complicated your machine is, the harder you have to twist it around to fit the binary-tree model of nested conditionals. And the more you twist, the harder it is to figure out what the original machine looked like while you're reading the code. In fact, since programmers often evolve code instead of designing it, I'd give strong odds that even the programmer who writes a series of nested conditionals doesn't know what kind of machine he's building, and can't prove that it's a well-formed minimal solution to the problem at hand.

      So it's not that nested conditionals are inherently evil. They're quite useful when applied properly. So are gotos. And like gotos, nested conditionals give programmers a way to disguise sloppy or inconsistent thinking as complex control flow. In other words, they can make code unnecessarily complex while looking perfectly reasonable every step of the way. That's why we should use them carefully and examine them critically, not just accept them blindly.

Defense of nested ifs
by drinkd (Pilgrim) on Jan 04, 2002 at 21:27 UTC
    While nested if statements look a mess, the unfortunate truth is that this is really the way decisions are made by humans.

    If my car starts, I'll go to work, If net nets up, I'll check email and read PM, If there's a good thread, I'll read it, etc. . . . ..

    The problem with a one-dimensional dispatch table, is that the operations in the outermost nests need to be replicated into much of the table entries, which require programatically building complicated hashes, futhur sacraficing readability.

    I'm not sure how to build a "multi-dimensional" dispatch table where say the "rows" have one set of functions and the "columns" have another, without programatically building the subroutines.

    say $daysactions{$morning_situation}{$afternoon_situation}=\&listofstufftodotodaygivenmorningandafternoonsituations;

    Any ideas?

    drinkd

      Using Ovid's example above ...
      sub whatFoo { if ($_[0] > 7) { return 'ONE' } else { return 'TWO' } } sub whatBarBaz { my ($bar, $baz) = @_; return 'BAR' if $bar; return 'BAZ' if $baz; return 'DEFAULT'; } sub no_op { } my %Dispatch = ( ONE => { BAR => \&firstBar, BAZ => \&firstBaz, }, TWO => { BAR => \&secondBar, BAZ => \&secondBaz, }, ); # Put the defaults in as NO-OPs, unless we already have a # default action foreach my $key (keys %Dispatch) { $Dispatch{$key}{DEFAULT} = \&no_op unless exists $Dispatch{$key}{DEFAULT}; } my $func = $Dispatch{whatFoo($foo)}{whatBarBaz($bar, $baz)}; $func->(@someArgs);
      Seems pretty clear-cut to me ... Plus, it's extensible. If you have a $quz you want to put in, add your QUZ entries to the dispatch and then change whatBarBaz() to allow for QUZ.

      ------
      We are the carpenters and bricklayers of the Information Age.

      Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

        I've been following this thread with some interest, as I just finished writing a CGI with a very unwieldy set of if / elsif / else statements, and I'm interested in how to put it on a more rational footing (posted below for anyone who wants a laugh). In simpler situations I've used a dispatch table. But here I couldn't work out how to pass (the right) arguments. As far as I can see, this is not handled explicitly above. I'd be interested in how to deal with the need (A) to pass different arguments to different subs; and (B)to pass different args to the *same* sub under different conditions ?

        My baobab-style if tree, so we can all see what I'm up against:
        if ($g{'Action'} eq 'MainPage') { MainPage($Persist, $UserName); } if (($g{'Action'} eq 'Course') or ($g{'Action'} eq 'EditCourse')) +{ $g{'CourseID'} = GetNewCourse() unless defined $g{'CourseID'}; UpdateTempData($g{'CourseID'}); EditCourse($Persist, $g{'CourseID'}, $g{'Return'}); } elsif ($g{'Action'} eq 'Create Course') { my $CourseID = GetNewCourse($UserName); UpdateTempData($CourseID); EditCourse($Persist, $CourseID, $g{'Return'}); } elsif ($g{'Action'} eq 'EditEvent') { EditEvent($Persist, $g{'CourseID'}, $g{'Return'}); } elsif ($g{'Action'} eq 'New Venue') { my $VenueID = GetNewVenue($Persist,$UserName); EditVenue($Persist, $VenueID, $g{'Return'}); } elsif (($g{'Action'} eq 'Venue') or ($g{'Action'} eq 'EditVenue')) + { EditVenue($Persist, $g{'VenueID'}, $g{'Return'}); } elsif ($g{'Action'} eq 'Course Overview') { CourseView($Persist, $UserName); } elsif ($g{'Action'} eq 'Venue Overview') { VenueView($Persist,$UserName); } elsif ($g{'Action'} eq 'DeleteCourse') { DeleteCourse($Persist, $g{'CourseID'}, $g{'Return'}); } elsif ($g{'Action'} eq 'Delete Course') { DeleteCourseYes($Persist, $g{'CourseID'}, $g{'Return'}); if ($g{'Return'} eq 'CourseView') { CourseView($Persist, $UserName); } else { MainPage($Persist, $UserName); } } elsif ($g{'Action'} eq 'Keep Course') { if ($g{'Return'} eq 'CourseView') { CourseView($Persist, $UserName); } else { MainPage($Persist, $UserName); } } elsif ($g{'Action'} eq 'DeleteEvent') { DeleteEvent( $Persist, $g{'EventID'}, $g{'Return'}); } elsif ($g{'Action'} eq 'Delete Event') { DeleteEventYes( $Persist, $g{'EventID'}); if ($g{'Return'} eq 'CourseView') { CourseView($Persist, $UserName); } elsif ($g{'Return'} eq 'EditEvent') { EditEvent($Persist, $g{'CourseID'},0,$g{'CourseTitle'}); } else { MainPage($Persist, $UserName); } } elsif ($g{'Action'} eq 'Keep Event') { if ($g{'Return'} eq 'CourseView') { CourseView($Persist, $UserName); } elsif ($g{'Return'} eq 'EditEvent') { EditEvent($Persist, $g{'CourseID'},0,$g{'CourseTitle'}); } else { MainPage($Persist, $UserName); } } elsif ($g{'Action'} eq 'DeleteVenue') { DeleteVenue($Persist, $g{'VenueID'}, $g{'Return'}); } elsif ($g{'Action'} eq 'Delete Venue') { DeleteVenueYes($Persist, $g{'VenueID'}, $g{'Return'}); if ($g{'Return'} eq 'VenueView') { VenueView($Persist, $UserName); } else { MainPage($Persist, $UserName); } } elsif ($g{'Action'} eq 'Keep Venue') { if ($g{'Return'} eq 'VenueView') { VenueView($Persist, $UserName); } else { MainPage($Persist, $UserName); } } elsif ($g{'Action'} eq 'Save Venue') { if (my $Errors = SaveVenue($Persist, $UserName)) { EditVenue($Persist, $g{'VenueID'}, $g{'Return'}, $Errors); } else { WriteVenue(); if ($g{'Return'} eq 'ReturnToEditEvent') { SaveVenue($Persist, $UserName); EditEvent($Persist, $g{'CourseID'}, $g{'Return'},$g{'C +ourseTitle'},0); } elsif ($g{'Return'} eq 'VenueView') { SaveVenue($Persist, $UserName); VenueView($Persist, $UserName); } else { SaveVenue($Persist, $UserName); MainPage($Persist, $UserName); } } } elsif ($g{'Action'} eq 'More Keywords') { EditCourse($Persist, $g{'CourseID'}, $g{'Return'}); } elsif ($g{'Action'} eq 'Next Step') { if (my @Errors = CourseErrorCheck()) { EditCourse($Persist, $g{'CourseID'}, $g{'Return'}, \@Error +s); } elsif (my $OldTitle = TitleCheck()) { UpdateTempData($g{'CourseID'}); QueryTitle($Persist, $g{'CourseID'}, $g{'Return'}, $OldTit +le); } else { UpdateTempData($g{'CourseID'}); EditCourseInfo($Persist, $g{'CourseID'}, $g{'Return'}); } } elsif ($g{'Action'} eq 'New Course') { my $g = GetFromFile($g{'CourseID'},\%g); %g = %$g; DestroyTempFile($g{'CourseID'}); $g{'CourseID'} = GetNewCourse(); MakeTempFile($g{'CourseID'}); UpdateTempData($g{'CourseID'}); EditCourseInfo($Persist, $g{'CourseID'}, $g{'Return'}); } elsif ($g{'Action'} eq 'Change Name') { UpdateTempData($g{'CourseID'}); EditCourseInfo($Persist, $g{'CourseID'}, $g{'Return'}); } elsif ($g{'Action'} eq 'More Links') { UpdateTempData($g{'CourseID'}); EditCourseInfo($Persist, $g{'CourseID'}, $g{'Return'}); } elsif ($g{'Action'} eq 'Another Page') { SaveUserPage($Persist, $g{'CourseID'},$g{'PageTitle'},$g{'Page +Text'},$g{'WeFormat'},$g{'CurrentPage'}); $g{'PageTitle'} = undef; $g{'PageText'} = undef; EditCourseInfo($Persist, $g{'CourseID'}, $g{'Return'},0,1); } elsif ($g{'Action'} =~ /^Edit Page (\d*)$/) { SaveUserPage($Persist, $g{'CourseID'},$g{'PageTitle'},$g{'Page +Text'},$g{'WeFormat'},$g{'CurrentPage'}); EditCourseInfo($Persist, $g{'CourseID'}, $g{'Return'}, $g{"Pag +eID_$1"},1); } elsif ($g{'Action'} =~ /^Delete Page (\d*)$/) { UpdateTempData($g{'CourseID'}); SaveUserPage($Persist, $g{'CourseID'},$g{'PageTitle'},$g{'Page +Text'},$g{'WeFormat'},$g{'CurrentPage'}); QueryDeleteUserPage($Persist, $g{'CourseID'}, $g{'Return'},$g{ +"PageID_$1"},$g{'CurrentPage'}); } elsif ($g{'Action'} eq 'Yes, Delete Page') { DeleteUserPage($g{'PageID'}); UpdateTempData($g{'CourseID'}); EditCourseInfo($Persist, $g{'CourseID'}, $g{'Return'},$g{'Curr +entPage'},1); } elsif ($g{'Action'} eq 'No, Keep Page') { UpdateTempData($g{'CourseID'}); EditCourseInfo($Persist, $g{'CourseID'}, $g{'Return'},$g{'Curr +entPage'},1); } elsif ($g{'Action'} eq 'No, Keep Page') { UpdateTempData($g{'CourseID'}); EditCourseInfo($Persist, $g{'CourseID'}, $g{'Return'},$g{'Curr +entPage'},1); } elsif ($g{'Action'} eq 'Proceed') { SaveUserPage($Persist, $g{'CourseID'},$g{'PageTitle'},$g{'Page +Text'},$g{'WeFormat'},$g{'CurrentPage'}); UpdateTempData($g{'CourseID'}); if (my $TearSheetError = CourseInfoErrorCheck()) { EditCourseInfo($Persist, $g{'CourseID'}, $g{'Return'},0,0, +$TearSheetError); } else { EditEvent($Persist, $g{'CourseID'}, $g{'Return'},$g{'Cours +eTitle'},0); } } elsif ($g{'Action'} eq 'Save Course') { SaveUserPage($Persist, $g{'CourseID'},$g{'PageTitle'},$g{'Page +Text'},$g{'WeFormat'},$g{'CurrentPage'}); UpdateTempData($g{'CourseID'}); if (my $TearSheetError = CourseInfoErrorCheck()) { EditCourseInfo($Persist, $g{'CourseID'}, $g{'Return'},0,0, +$TearSheetError); } else { SaveCourse($Persist, $g{'CourseID'}, $g{'Return'}); if ($g{'Return'} eq 'CourseView') { CourseView($Persist, $UserName); } else { MainPage($Persist, $UserName); } } } elsif ($g{'Action'} eq 'Make Venue') { my $VenueID = GetNewVenue($Persist,$UserName); EditVenue($Persist, $VenueID,'ReturnToEditEvent',0,$g{'CourseI +D'},$g{'CourseTitle'}); } elsif ($g{'Action'} eq 'Add Event') { if (my $Errors = CheckEvent($g{'CourseID'})) { EditEvent($Persist, $g{'CourseID'}, $g{'Return'},$g{'Cours +eTitle'},$Errors); } else { AddEvent($Persist, $g{'CourseID'}, $g{'CourseTitle'}); EditEvent($Persist, $g{'CourseID'}, $g{'Return'},$g{'Cours +eTitle'},0); } } elsif ($g{'Action'} eq 'Save All') { my $Errors = CheckEvent($g{'CourseID'}); my $DateError = 0; my $VenueError = 0; my $DuplicateError = 0; if ($Errors) { $DateError = 1 if grep {$_ eq 'Date'} @$Errors; $VenueError = 1 if grep {$_ eq 'Venue'} @$Errors; $DuplicateError = 1 if grep {$_ eq 'Duplicate'} @$Errors; } if ($DateError + $VenueError == 1) { EditEvent($Persist, $g{'CourseID'}, $g{'Return'},$g{'Cours +eTitle'},$Errors); } else { AddEvent($Persist, $g{'CourseID'}, $g{'CourseTitle'}) unle +ss ($DuplicateError == 1 or $DateError + $VenueError == 2); if (stat "$filepath/tempfiles/$g{'CourseID'}") { UpdateTempData($g{'CourseID'}); SaveCourse($Persist, $g{'CourseID'}, $g{'Return'}); } if (defined $g{'Return'} and $g{'Return'} eq 'CourseView') + { CourseView($Persist, $UserName); } else { MainPage($Persist, $UserName); } } } else { MainPage($Persist, $UserName, $UserCookie); }


        § George Sherston
Re: Why I Hate Nested If-Else blocks
by Stegalex (Chaplain) on Jan 04, 2002 at 19:22 UTC
    Why rant about nested if-elses? There is nothing intrinsically wrong with them. When in doubt, I always hit shift-5 in vi to check that my {'s match my }'s. In fact, I am going out on a limb to say that I LIKE nested if-elses. I really do. Here, choke on this:
    if ($a) { if ($b) { } else { } } else {if ($c) { } else { } }

    I like chicken.