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

Dear Masters,
I have 5 functions (A,B,..E) which need to be called in various combinations, from user input. Currently I do it in the following hard coded way:
#!/usr/bin/perl -w my $func_com = $ARGV[0] || 'ABC'; #user input my @some_val = (0..10); my @all_res; # Now begin hard-coding all possible function call # with else-if if ($func_com eq 'A') { my @tempA = func_A(@some_val); push @all_res, [\@tempA]; } elsif ($func_com eq 'AB'){ my @tempA = func_A(@some_val); my @tempB = func_B(@some_val); push @all_res, [\@tempA, \@tempB]; } elsif ($func_com eq 'ABC'){ my @tempA = func_A(@some_val); my @tempB = func_B(@some_val); my @tempC = func_C(@some_val); push @all_res, [\@tempA, \@tempB, \@tempC]; } ##...etc, for all possible function combination of ## A,B, ...E # Do something with @all_res #------------------- # Functions collection #------------------- sub func_A { my @arr = @_; my @output; # do sth with @arr return @output; } sub func_B { my @arr = @_; my @output; # do sth with @arr return @output; } # .... etc until func_E
The problem I had is that: is there an alternative construct to avoid hard coding all the possible else-if function combinations above?
Because the functions will grow to more than just 5 above.

---
neversaint and everlastingly indebted.......

Replies are listed 'Best First'.
Re: Howto avoid large hard-coded else-if function calls
by BrowserUk (Patriarch) on Jun 24, 2007 at 08:05 UTC

    A dispatch table of the funcs:

    #! perl -slw use strict; my %dispatch = ( A => sub{ map{ $_ +1 } @_ }, B => sub{ map{ $_ *2 } @_ }, C => sub{ map{ $_**2 } @_ }, D => sub{ map{ $_ +3 } @_ }, E => sub{ map{ $_ *5 } @_ }, ); my $func_com = $ARGV[ 0 ] || 'ABC'; my @some_val = 1 .. 10; @some_val = ( $dispatch{ $_ } || sub{ @_ } )->( @some_val ) for split '', $func_com; print "$func_com: [ @some_val ]"; __END__ c:\test>junk6 ABC: [ 16 36 64 100 144 196 256 324 400 484 ] c:\test>junk6 A A: [ 2 3 4 5 6 7 8 9 10 11 ] c:\test>junk6 B B: [ 2 4 6 8 10 12 14 16 18 20 ] c:\test>junk6 AB AB: [ 4 6 8 10 12 14 16 18 20 22 ] c:\test>junk6 C C: [ 1 4 9 16 25 36 49 64 81 100 ] c:\test>junk6 AC AC: [ 4 9 16 25 36 49 64 81 100 121 ] c:\test>junk6 ABCDE ABCDE: [ 95 195 335 515 735 995 1295 1635 2015 2435 ] c:\test>junk6 F F: [ 1 2 3 4 5 6 7 8 9 10 ] c:\test>junk6 FA FA: [ 2 3 4 5 6 7 8 9 10 11 ]

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Howto avoid large hard-coded else-if function calls
by FunkyMonk (Bishop) on Jun 24, 2007 at 08:04 UTC
    Use a dispatch table to map your categories (A .. E) to functions and look for you category in $func_com.

    Something like...

    my %dispatcher = ( A => \&func_A, B => \&func_B, C => \&func_C, #etc ); my @temp_results; for my $cat ( 'A' .. 'E' ) { push @temp_results, [ $dispatcher{$cat}->( @some_val ) ] if $func_com =~ m/$cat/; # index is better } push @all_res, \@temp_results; # Do something with @all_res
Re: Howto avoid large hard-coded else-if function calls
by vkon (Curate) on Jun 24, 2007 at 07:56 UTC
    put all your functions into hash and call from there:
    my %funcs = ('A'=>\&func_a, 'ABC'=>\&func_c); #etc #... #and then call it as push @res, $funcs{$func_com}->(@someval);

    update checking for existance of function in the hash was left as an excercise for a reader
    :)

Re: Howto avoid large hard-coded else-if function calls
by grinder (Bishop) on Jun 24, 2007 at 10:22 UTC

    I think a dispatch table will rapidly become unwieldy if you need to consider all possible function call combinations of A, B, C, D and E. Assuming A must always be called first, and C must be called between B and D (if present), and so on, you are looking at the power set of A .. E. And the size of the power set of a set increases exponentially.

    Here's all the different ways you can call functions A to E:

    perl -MData::PowerSet -le 'my $p = Data::PowerSet->new(qw(A B C D E)); + print "@$_" while $_= $p->next' # produces : A B C D E B C D E A C D E C D E A B D E B D E A D E D E A B C E B C E A C E C E A B E B E A E E A B C D B C D A C D C D A B D B D A D D A B C B C A C C A B B A

    Add three more function and the size of your dispatch table grows to 256 entries... and it gets much worse if you can call any of A to E in any order.

    I think it would be much better to just split your 'ABC' input into separate characters, and use a simple dispatch to deal with the five (or more) elementary cases:

    my %dispatch = ( A => \&func_a, B => \&func_b, C => \&func_c, D => \&func_d, E => \&func_e, ); for my $token (split //, $func_com) { push @all_res, [$dispatch{$token}->(@some_val)]; }

    This way it is as trivial to deal with 'ABC', 'AD', 'BCE' as it is to deal with 'AABD' or 'BEAD'.

    • another intruder with the mooring in the heart of the Perl

Re: Howto avoid large hard-coded else-if function calls
by shmem (Chancellor) on Jun 24, 2007 at 08:09 UTC
    Use a dispatch table, e.g.
    my %table = map { $_ => \&{'func_'.$_} } 'A'..'E'; for (split //, $func_com) { push @all_res, [ $table{$_}->(@some_val) ]; }

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: Howto avoid large hard-coded else-if function calls
by friedo (Prior) on Jun 24, 2007 at 12:47 UTC
    This is just a shot in the dark, but maybe you should use a dispatch table. :)
Re: Howto avoid large hard-coded else-if function calls
by jdporter (Paladin) on Jun 25, 2007 at 11:51 UTC

    Here's another way. Its suitability is something you'd have to decide...

    $func_com =~ y/ABCDE//cd; $func_com =~ s/(.)/func_$1();/g; eval $func_com;
    A word spoken in Mind will reach its own level, in the objective world, by its own weight

      Here's another technique which is kind of like a combination of both the dispatch table, as presented elsewhere in this thread, and interpreting the 'ABC' string directly as code. I think it's generally better, especially in an application architecture which is already object oriented.

      { package Funcs; use Carp; sub A { print "A!\n" } sub B { print "B!\n" } sub C { print "C!\n" } sub D { print "D!\n" } sub E { print "E!\n" } sub AUTOLOAD { our $AUTOLOAD; carp "no $AUTOLOAD"; } } # now, wherever $func_com comes from... Funcs->$_() for split //, $func_com;

      Of course, you may have other ways of parsing $func_com, rather than just by single characters; and other ways of validating it in addition to making the calls and barfing on non-existent methods.

      A word spoken in Mind will reach its own level, in the objective world, by its own weight