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

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

I perused perlmonks and google in search of a dispatch table. The examples I found didn't pass arguments with the named subroutines. I think I have it solved, but would like confirmtion.

#!/usr/bin/perl -w use strict; $|++; my $fruit = [ qw|apple orange pear| ]; my $dispatch = { add => sub { return add_entry( $fruit ) }, }; print scalar localtime, "\n"; sleep 1; print &{ $dispatch->{add} }, " <-- from sub \n";; sleep 1; print scalar localtime, "\n"; sub add_entry{ print scalar localtime, " <-- in sub \n"; sleep 1; return scalar localtime; } __END__ produces: Sat Apr 27 19:00:21 2002 Sat Apr 27 19:00:22 2002 <-- in sub Sat Apr 27 19:00:23 2002 <-- from sub Sat Apr 27 19:00:24 2002
    Two questions:
  1. Is the test valid?
  2. Am I missing some pitfall that will bite me later?

Thank You,
Charles K. Clarkson
Clarkson Energy Homes, Inc.

Replies are listed 'Best First'.
Re: passing arguments in a dispatch table
by abstracts (Hermit) on Apr 28, 2002 at 02:41 UTC
    Here is an example of how to pass args to subs in a dispatch table. I didn't see you pass any args in your example.
    my $dispatch = { add => sub { return $_[0] + $_[1] }, mul => sub { return $_[0] * $_[1] }, div => sub { return $_[0] / $_[1] } }; print "2 + 3 = " . $dispatch->{add}->(2,3) . "\n"; print "2 * 3 = " . $dispatch->{mul}->(2,3) . "\n"; print "2 / 3 = " . $dispatch->{div}->(2,3) . "\n"; __END__ 2 + 3 = 5 2 * 3 = 6 2 / 3 = 0.666666666666667

      You misunderstand. I was not trying to pass arguments after the dispatch table was defined. I was trying to pass arguments predefined in the dispatch table. In the example I gave $fruit was the argument. If I use \&add_entry( $fruit ), perl will run add_entry( $fruit ) immediately and return a reference to it's return. But $fruit might change before add_entry is dispatched or between dispatched calls. The point was to define the arguments in the table not afterward.

      The problem was that this solution is skipped in the dipatch table examples I found online and in books. Some examples intimated that passing arguments from the table was difficult. I found the answer so easy, I thought I had missed something.


      HTH,
      Charles K. Clarkson
      Clarkson Energy Homes, Inc.
        Whats the point of declaring args in the dispatch table again? If the args are going to change between dispatch calls, then just call the dispatch ->(@args); if they arent going to change, then define defaults in the function itself.
Re: passing arguments in a dispatch table
by rinceWind (Monsignor) on Apr 28, 2002 at 10:23 UTC
    CharlesClarkson,

    I think that you are looking at a closure. As such, data are available as lexicals in the context in which your anonymous sub is defined. Usually you will want $fruit to go out of scope deliberately.

    Also, add_entry does nothing with its argument, hence you would not be able to observe a difference:

    Warning: untested code

    #!/usr/bin/perl -w use strict; $|++; my $dispatch; { my $fruit = [ qw|apple orange pear| ]; $dispatch = { add => sub { return add_entry( $fruit ) }, }; } print scalar localtime, "\n"; sleep 1; print &{ $dispatch->{add} }, " <-- from sub \n";; sleep 1; print scalar localtime, "\n"; sub add_entry{ print scalar localtime, "$_[0] <-- in sub \n"; sleep 1; return scalar localtime; } __END__

      Frankly, I think I need an object, but I'm reluctant to use one or a closure. I am introducing a dispatch table as a new concept on a perl beginners mailing list (PBML on Yahoo!). The original script used @book as a global variable. I changed it to $friut for the example above.

      I haven't added the response yet. I do a ton of editing before replying, so I'm still able to scrap this approach.

          Currently, I'm here:
      sub process { my ( $choice, $book ) = @_; my $dispatch = { 1 => \&search_menu, 2 => sub { return \&edit_name($book) }, 3 => sub { return add_entry($book) }, 4 => sub { return delete_entry($book) }, 5 => \&save_default, 6 => \sub{ print "Goodbye!\n"; die; }, e => \sub{ print "Goodbye!\n"; die; }, }; print &{ $dispatch->{ $choice } }; }

      It's a direct move from a group of if - elsif blocks that did the same thing. That's why I'd like to call the subs the same way now. I can introduce better concepts in later posts. I consider this an intermediate approach. Later, $dispatch will be passed to process()as well (which is why I use a hash reference instead of a hash). To help with processing the search menu. $book is reference to @book initally taken from a flat file. memoize immediately comes to mind, but too many beginners shun modules and I don't want to lose my audience.

      I initially scrapped an idea of using a BEGIN block. Perhaps I could combine the two and grab the array reference in each subroutine as someone else mentioned. Though I find using the dispatch table as a source for the arguments that are passed more powerful.

      BEGIN { open FH, 'my_db' or die "Cannot open my_db: $!"; my @book = split /,/, <FH>; sub address_book { return \@book } }

      HTH,
      Charles K. Clarkson
      Clarkson Energy Homes, Inc.
        You say "Frankly, I think I need an object, but I'm reluctant to use one or a closure.", but you're using closures in your code segment above.

        You might want to consider this approach:

        my $dispatch = { 1 => sub { search_menu() }, 2 => sub { my $book = shift; edit_name($book) }, 3 => sub { my $book = shift; add_entry($book) }, 4 => sub { my $book = shift; delete_entry($book) }, 5 => sub { save_default() }, 6 => sub { print "Goodbye!\n"; exit }, e => sub { print "Goodbye!\n"; exit } }; sub process { my ( $choice, $book ) = @_; print $dispatch->{$choice}->($book); }
        Here, we're removing the closure completely, and passing the args directly to the appropriate sub. Did I completely miss what you're trying to do?
Re: passing arguments in a dispatch table
by samtregar (Abbot) on Apr 28, 2002 at 01:56 UTC
    Looks OK to me, although I don't really know what you're testing. %$dispatch is indeed a hash of CODE-refs, which you can use a "dispatch table." But you knew that. What was the question again?

    -sam

      Gee. You'd think you weren't looking over my shoulder the whole day and only had this one message to figure out what was going on.    :)

      If we change the dispatch table:
      my $dispatch = { add => \&add_entry( $fruit ), };
      We get this result:
      Sun Apr 28 01:23:38 2002 <--   in sub 
      Sun Apr 28 01:23:39 2002
      Not a CODE reference at aaaa.pl line 17.
      

      The test in the original example allowed me to see that the times were in the correct order. If perl was running the sub immediately, the times would be out of order. I was unsure if this test made sense. Hence the question.


      HTH,
      Charles K. Clarkson
      Clarkson Energy Homes, Inc.