On Nov 12, 2005, at 11:18 PM, Ken Williams wrote:

Yeah, I know, but I posted it because I didn't really like the other solutions; while they use "better" (and more complicated) techniques, they won't actually solve the OP's problem. The "pretend you have methods instead of subroutines" solution is just wishful thinking, and the "dispatch tables" solution seems overengineered and won't work unless the specific list of allowed functions (rather than just a naming-scheme pattern or similar) is known to the dispatcher.

The subroutines have to be known anyway. Unless, of course, you're going to let the eval() just crash...

James, if you don't want to use an eval, and you need to call functions by name this way, you can just use a symbolic reference:

 &{$subroutine_name}($hashref);

When in doubt, benchmark it:

    #!/usr/bin/perl

    use strict;
    use warnings;

    use Benchmark qw(:all);

    my %dispatch = (
        'a' => \&a,
        'b' => \&b,
        'c' => \&c,
    );

    my $hashref = {
        'foo' => 1,
        'bar' => 2,
        'baz' => 3,
    };

    timethese(100000, {

        'Dispatch Table' => sub {
for my $sub_name ('a', 'b', 'c', 'b', 'c', 'a', 'a', 'c', 'b') {
                    $dispatch{$sub_name}->($hashref);
                }
            },

        'Symref' => sub {
for my $sub_name ('a', 'b', 'c', 'b', 'c', 'a', 'a', 'c', 'b') {
                    {
                        no strict 'refs';
                        &$sub_name($hashref);
                    }
                }
            },

        'Eval' => sub {
for my $sub_name ('a', 'b', 'c', 'b', 'c', 'a', 'a', 'c', 'b') {
                    eval "$sub_name(\$hashref)";
                }
            },

        });

    sub a {
        my ($hr) = @_;
        my $foo = "\t" . join(",", keys(%$hr)) . "\n";
    }

    sub b {
        my ($hr) = @_;
        my $foo = "\t" . join(",", keys(%$hr)) . "\n";
    }

    sub c {
        my ($hr) = @_;
        my $foo = "\t" . join(",", keys(%$hr)) . "\n";
    }

The results I get are:

Benchmark: timing 100000 iterations of Dispatch Table, Eval, Symref...
Dispatch Table: 17 wallclock secs (14.77 usr + 0.29 sys = 15.06 CPU) @ 6640.11/s (n=100000) Eval: 155 wallclock secs (139.45 usr + 3.23 sys = 142.68 CPU) @ 700.87/s (n=100000) Symref: 18 wallclock secs (17.49 usr + 0.35 sys = 17.84 CPU) @ 5605.38/s (n=100000)

The Eval results are no surprise - it has to compile the string for each iteration, which is a huge performance hit.

The symref approach has some overhead too, I suppose because of the extra scoping block for "no strict", but it's relatively small. Still, it's enough to make using symrefs a questionable idea.

I have to admit, I'm puzzled about why you'd call the dispatch table "over engineered". There's a difference in syntax when the sub is called, but that's trivial. The only substantial difference is the addition of the dispatch table itself, which isn't exactly a herculean effort. The result is both the cleanest and fastest of the three approaches.

sherm--

Cocoa programming in Perl: http://camelbones.sourceforge.net
Hire me! My resume: http://www.dot-app.org

Reply via email to