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

Could someone give me some advice on writing a switch-like statement using a foreach loop that would call various subroutines.
something like:
foreach (@do_something) { if $_ = "do_run" call do_run(); if $_ = "do_walk" call do_walk(); if $_ = "do_skip" call do_skip(); }
Thanks in advance for your help.

Replies are listed 'Best First'.
Re: switch statement for subroutines?
by Masem (Monsignor) on Jul 27, 2001 at 23:15 UTC
    Use a hash; much easier to read than long if statements, and more extendable:
    my %subhash = { do_run => \&do_run, do_walk => \&do_walk, do_skip => \&do_skip }; foreach ( @do_something ) { if ( defined $subhash{ $_ } ) { &{ $subhash{ $_ } }(); } }

    -----------------------------------------------------
    Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain

      Hello,

      I noticed you used the curley brackets instead of the regular brackets in the hash assignment, but that's okay ;)

      I guess doing the following should be more readable, no?

      foreach ( @do_something ) { defined $subhash{$_} and $subhash{$_}->() } # OR foreach ( @do_something ) { $subhash{$_}->() if defined $subhash{$_} }
      This way we avoid most of the ugly punctuation marks.

      Aziz

(ar0n: use Switch) Re: switch statement for subroutines?
by ar0n (Priest) on Jul 27, 2001 at 23:05 UTC
    CPAN is your friend, grasshopper:
    use Switch; foreach my $val (@array) { switch ($val) { case "do_walk" { do_walk() } case "do_run" { do_run(); } case "do_skip" { do_skip(); } } }

    ar0n ]

      This module is rather fragile and many would never allow into their production code. If you use it, don't be surprised if your code breaks later when you make what looks like a rather innocent change.

              - tye (but my friends call me "Tye")
Re: switch statement for subroutines?
by dragonchild (Archbishop) on Jul 27, 2001 at 23:13 UTC
    ar0n is perfectly correct, however there's a neat Perl-ism in your example.

    &$_ for @do_something;

    This, of course, assumes that all the strings in @do_something have functions associated with them. Otherwise, Perl will die and complain.

    However, you can get past that by doing something like:

    my $foo = Some::Class->new; $foo->$_ if $foo->can($_) for @do_something;
    *grins* A really neat thing about Perl OO.
Re: switch statement for subroutines?
by bikeNomad (Priest) on Jul 27, 2001 at 23:17 UTC
    { # localize the lack of B&D no strict 'refs'; foreach my $sub (@do_something) { $sub->() } }
Re: switch statement for subroutines?
by HyperZonk (Friar) on Jul 28, 2001 at 04:19 UTC
    All of the code provided in reply has been workable, but I notice that no one has mentioned a couple of errors in your code to you. While good solutions have been given to solve your current dilemma, I'd like to point the errors out to you so they don't bite you at a later date.

    In your code, you attempt to test with $_ = "do_run". This is actually assigning 'do_run' to the variable. The string test operator is eq, so you want if $_ eq 'do_run'. (For numeric comparison, use ==). Also, you do not call a subroutine in perl -- just use the subroutine name.

    When 'if' begins the line and is followed by a comparison, the comparison requires parentheses. Also, the block to be run on true needs to have brackets. Both of these can be eliminated in the switched around version (see below) due to precedence and syntax.

    A final note (this isn't a bug, just a style issue) ... you are using double-quotes, which does variable interpolation within the string. Since you are not interpolating variables, single quotes will suffice. So the way a string comparison might look is:
    if ($_ eq 'do_run') { do_run() }
    The alternative style ("switched around" as I called it above) would appear as:
    do_run() if $_ eq 'do_run';

    -HZ
Re: switch statement for subroutines?
by enoch (Chaplain) on Jul 28, 2001 at 01:34 UTC
    I believe I first saw this in Programming Perl.
    SWITCH: { do_run() last SWITCH if $_ eq "do_run"; do_walk() last SWITCH if $_ eq "do_walk"; do_skip() last SWITCH if $_ eq "do_skip"; }
    Jeremy
Re: switch statement for subroutines?
by petral (Curate) on Jul 29, 2001 at 13:28 UTC
    Since no one's mentioned it, here's one more WTDI:
    for (@do_something) { /do_run/ ? do_run() : /do_walk/ ? do_walk() : /do_skip ? do_skip() : next; # ?!? catch none-of-the-above either error or default }
      p