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

Hi All, Lately I have started to use a new idiom when I want to do "switch"-like things. I was wondering if this idiom might have some undesirable side effects that I might not know about. Such as a lot of overhead or a big performance hit. What I used to do was something like this:
my $test = $foo->bar(); if($test=~/good.*food/){ # do something } elsif($test eq "tacos"){ # do something else } elsif(ref($test) eq "Burrito"){ # do something else } else{ # print out some message }
What I have started doing is something like below. For me it is easier to read.
for($foo->bar()){ if(/good.*food/){ # do something } elsif($_ eq "tacos"){ # do something else } elsif(ref eq "Burrito"){ # do something else } else{ # print out some message } }
Thoughts?

Replies are listed 'Best First'.
Re: Pseudo-switch question
by edan (Curate) on Jul 22, 2003 at 15:13 UTC

    Well, the example given in perlfaq7 (perldoc -q switch) is a bit easier for me to read:

    SWITCH: for (ref $whatchamacallit) { /^$/ && die "not a reference"; /SCALAR/ && do { print_scalar($$ref); last SWITCH; }; /ARRAY/ && do { print_array(@$ref); last SWITCH; }; /HASH/ && do { print_hash(%$ref); last SWITCH; }; /CODE/ && do { warn "can't print function ref" +; last SWITCH; }; # DEFAULT warn "User defined type skipped"; }

    Seems pretty much the same as what you're doing, as far as functionality, just a little prettier...

    Update: Added more thoughts:

    I can't see any performance hit. All you're really doing is assigning your test to $_ so that you can make testing it prettier.

    --
    3dan

      For what's it's worth, I prefer either of the versions in the original node to this by a factor of about 1000.

      I'd love to see the look on your replacement's face when they see such code.

      You prefer having to come up with a label and then repeating "last LABEL" all over the place to a simple elsif ?? My mind boggles. Really.

      And that indentation scheme is atrocious! (Okay, now I sound like that guy in Airplane!... Johnny, talking to the captain's wife, ya know?)

      But, in all sincerity, I find it hard to express how distasteful I find that code. I boggled at it when it showed up in the documentation (I think it predates 'pod'). It has just gotten uglier over the years.

      If there were some powerful, compelling advantage to it, then I might have a small amount of appreciation for it. I realize that it allows you to "fall through" somewhat like a switch/case, but it doesn't even emulate that very well.

      [ And even beyond all of the awful style problems, the code isn't a very good example. The regexen are unanchored (and a nice 'eq' makes more sense anyway). ref should be replaced with isa or something from Scalar::Util. And these just point out the limitations with this syntax: Try to fix the code and this syntax will fight you. And then passing the hash to the print function as a big list? ... ]

                      - tye
Re: Pseudo-switch question
by simonm (Vicar) on Jul 22, 2003 at 15:32 UTC

    This looks like good, idiomatic Perl code, fletcher -- nothing to worry about.

    The one "side effect" is that it temporarily exposes the test value in the global $_, such that it could be accessed from within a subroutine called from within one of your # do something blocks, although this isn't likely to cause problems in properly written code.

      And of course, if you use this in a sub you've clobbered $_. Add a
      local $_;
      previous to your preferred switch method and your callers will thank you.
        Doesn't "for" always automatically localize "$_" like in this example?
        my @list = qw(I Like tacos); for (@list) { print "A:".$_."\n"; for ("Hello") { print "B:".$_."\n"; } print "C:".$_."\n"; }
        It is good that you considered that type of problem, but in this case, it is harmless.

        My rule of thumb is that when using a for/foreach loop, you do not need to localize $_, but if you are using a while(<>) loop, you must localize it.

        This is because the for/foreach loop is already doing the local step for you, but it is the only time that this happens.

•Re: Pseudo-switch question
by merlyn (Sage) on Jul 22, 2003 at 15:11 UTC

      Because...

      1. it can break correct, otherwise working code
      Well. I guess one reason is enough in this case.

      Update: I'd rather it were named Acme::Switch. It isn't really appropriate for use in production, IMO.

                      - tye
        Mind giving an example? I've never seen it break code, unless you try to break it on purpose. And just because a module is implemented a bit oddly dosen't mean it's a gag module.
      I would but I can't guarentee that all the machines that will run my scripts will have perl 5.8 or the filter modules that "Switch" requires.
Re: Pseudo-switch question
by nite_man (Deacon) on Jul 22, 2003 at 15:25 UTC
Re: Pseudo-switch question
by Solo (Deacon) on Jul 22, 2003 at 21:38 UTC
    perlsyn has a section devoted to SWITCH-iness. Personally, I prefer the short-circuit with && styles.

    --Solo

    --
    There are a lot of command ships. Keep your distance though, Chewie, but don't look like you're trying to keep your distance. (Chewie barks a question.) I don't know. Fly casual.
Re: Pseudo-switch question
by Tommy (Chaplain) on Jul 23, 2003 at 05:15 UTC
    I like switching like this...
    my($favorite_color) = ''; # get some kind of input here
    
    print {
          'red'    => sub { 'the color is red.'    }
          'yellow' => sub { 'the color is yellow.' }
          'blue'   => sub { 'the color is blue.'   }
          'null'   => sub { 'I do not know that color' }
       }
          ->{ $favorite_color || 'null' }
    
    --
    Tommy Butler, a.k.a. TOMMY
    

      Oops! Should have checked that code! Curses! Please excuse that poor form; I'm sorry!

      Correction

      my($favorite_color) = ''; # get some kind of input here
      
      print &{{
            'red'    => sub { 'the color is red.'    },
            'yellow' => sub { 'the color is yellow.' },
            'blue'   => sub { 'the color is blue.'   },
            'null'   => sub { 'I do not know that color' }
         }
            ->{ $favorite_color || 'null' }}
      
      
      --
      Tommy Butler, a.k.a. TOMMY
      
      Concering the correction - you could just append ->(). The null thing doesn't work if you get a key not contained in the hash, though. I'd do something like
      my($favorite_color) = ''; # get some kind of input here my $action = { 'red' => sub { 'the color is red.' }, 'yellow' => sub { 'the color is yellow.' }, 'blue' => sub { 'the color is blue.' }, }->{$favourite_color} || sub { 'I do not know that color' }; print $action->();
      You can arrange the same semantics in different ways, of course. I chose it like this since I was aiming to keep the "default case" together with the rest.

      Makeshifts last the longest.

Re: Pseudo-switch question
by Aristotle (Chancellor) on Jul 23, 2003 at 10:11 UTC
    Personally, I often nest ternaries when faced with such a case.
    $foo ? do { bar($_) for 1..3; } : $baz eq $quux ? (warn, return -1) : $answer == 42 ? print "but what's the question?\n" : print "who knows\n";

    To my sense of aesthetics, this is quite pleasing. Of course, it only works well if the code for each case isn't very long, otherwise it breaks apart visually.

    The only thing I don't like about it is the default case's treatment in formatting - I can't decide whether to line it up, indent it, or how to otherwise make it obvious as default case. Right now, I'm thinking unconditionally wrapping it in a do block and leaving it at the same indentation level as the conditionals (which is where it logically belongs - it just doesn't seem to stand out enough by itself). As in

    $foo ? do { bar($_) for 1..3; } : $baz eq $quux ? (warn, return -1) : $answer == 42 ? print "but what's the question?\n" : do { print "who knows\n" };
    I quite like this solution now that I'm looking at it. Maybe I can finally make up my mind.

    Makeshifts last the longest.