bradcathey has asked for the wisdom of the Perl Monks concerning the following question:

I do a lot of conditional testing, such as:
if ($x == 1) { $abc = 1 } elsif ($x == 2) { $def = 1 } elsif ($x == 3) { $xyz = 1 } else { $nothing = 1 }
but in reading Prog. Perl (p. 125) I see this could send me to the confessional. So, I tried:
SWITCH: { if ($x == 1) { $abc = 1; last SWITCH; }; if ($x == 2) { $def = 1; last SWITCH; }; if ($x == 3) { $xyz = 1; last SWITCH; }; $nothing = 1; }
or
SWITCH: for ($x) { if (1) { $abc = 1; last SWITCH; }; if (2) { $def = 1; last SWITCH; }; if (3) { $xyz = 1; last SWITCH; }; $nothing = 1; }
Sure it looks nicer, but in speed tests, depending on how $x is set, this later structure always slower, and in some cases significantly.

My question, what am I missing here? The if/else structure actually looks cleaner to me and if it's faster...

Thanks, monks of greater XP.

bradcathey

Replies are listed 'Best First'.
Re: Use of SWITCH vs IF ELSE in speed and readability
by CombatSquirrel (Hermit) on Aug 30, 2003 at 16:07 UTC
    bradcathey, your fist piece of code is perfectly alright, the second one is an emulated switch statement and the third one is wrong. It would have to be if ($_ == 1). Just execute the following code and you'll see it:
    $x = 3; SWITCH: for ($x) { if (1) { print '1'; last SWITCH; }; if (2) { print '2'; last SWITCH; }; if (3) { print '3'; last SWITCH; }; print 'caught'; }
    It's going to print 1, because 1 is true, so that the if (1) statement is always executed.
    Hope this helped.
    CombatSquirrel.
    Entropy is the tendency of everything going to hell.
      Thanks CombatSquirrel, your answers are always helpful, and innovative.

      So, there is nothing wrong with using the if/else structure? Just don't know why the Perl book says "horrors!"
        You know, TIMTOWDI. And I personally prefer the if/elsif/else structures - maybe I'm a bit too much of a C(++) programmer ;-)
        Cheers,
        CombatSquirrel.
        Entropy is the tendency of everything going to hell.
Re: Use of SWITCH vs IF ELSE in speed and readability
by Zaxo (Archbishop) on Aug 30, 2003 at 16:29 UTC

    Another approach is to use a dispatch table - a hash of code references,

    my $dtbl = { 1 => sub { $abc = 1 }, 2 => sub { $def = 1 }, 3 => sub { $xyz = 1 }, }; exists $dtbl->{$x} ? $dtbl->{$x}() : $nothing = 1;

    After Compline,
    Zaxo

      Just a style difference, but I really dislike using the ternary ? : for statements (code which is of irrelevant or void context, performed for their side-effects). It's all just code and doesn't matter inside the machine, but to me, the ternary is intended to express a selection of either one expression or the other.

      --
      [ e d @ h a l l e y . c c ]

Re: Use of SWITCH vs IF ELSE in speed and readability
by perrin (Chancellor) on Aug 30, 2003 at 18:06 UTC
    Unless it's in a critical section of code, use whichever one seems most readable in your context. It's a tough call in this example, but sometimes I find the regex approach (what you were trying to do on the 3rd one) the most readable. When speed really counts, just use the elsifs. They're not so bad.
Re: Use of SWITCH vs IF ELSE in speed and readability
by BrowserUk (Patriarch) on Aug 31, 2003 at 08:03 UTC

    For simple conditions and small blocks of code, there's not too much wrong with an if/elsif cascade, except perhaps that if all your conditions are integers, especially a contiguous range of integers, it means that you are sometimes testing more conditions than necessary.

    For small, contiguous ranges of conditions, I prefer a array-based dispatch table. This can also be coded inline, although having the condition at the end may be seen as a disadvantage:

    ( sub{ $abc == 1 }, sub{ $def == 1 }, sub{ $xyz == 1 }, )[ $x - 1 ]->();

    Somewhat obfuscate, but quite clear and efficient for small contiguous ranges. The absence of a simple 'default' mechanism is a shame.

    For discontiguous ranges, a similar arrangment using an anonymous hash can work

    ${{ 1 => sub{ $abc == 1 }, 3 => sub{ $def == 1 }, 5 => sub{ $xyz == 1 }, }}{ $x }->();

    The biggest problem, besides the obscurity of the construction, is the absence of a simple 'default' mechanism.

    Of course they don't have to be inline, and can used a named array or hash, which also gives the possibility for providing for a default.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller
    If I understand your problem, I can solve it! Of course, the same can be said for you.

Re: Use of SWITCH vs IF ELSE in speed and readability
by TomDLux (Vicar) on Aug 30, 2003 at 18:28 UTC

    If you're really comparing based on small integers, and really modifying the value of variables in a similar way, I would consider using an array of scalar references .... or re-writing to use an array or hash to replace the scalars:

    my @table = ( \$abc, \$def, \$wyz ); $table[$x] = 1; # or my @categories; $categories[$x]++; # or my %categories; $categories{$x} = code2value( $x );

    Not a religious thing, but it does have the advantage of automatically handling new categories, if you can arrange for the returned values to remain small integers.

    --
    TTTATCGGTCGTTATATAGATGTTTGCA