如何使用哈希在Perl中创建回调函数(调度表)?

时间:2011-08-30 01:17:28

标签: perl function hash callback dynamic-function

我想调用一个动态调度其他函数的主控制器函数,如下所示:

package Controller;

my %callback_funcs = ();

sub register_callback{
   my ($class,$callback,$options) = _@;
   #apppend to %callback_funcs hash ... ?
}

sub main{
%callback_funcs = ( add => 'add_func', rem => 'remove_func', edit => 'edit_func');  
  while(<STDIN>){
     last if ($_ =~ /^\s*$/);
     if($_ == 'add' || _$ == 'rem' || _$ == 'edit'){
        $result = ${callback_funcs['add']['func']}(callback_funcs['add']['options']);
     }
  }
}

sub add_func{
...
}

有一点需要注意,sub是在其他模块中定义的,因此回调必须能够引用它们...... 我很难让哈希合适!

3 个答案:

答案 0 :(得分:11)

因此,可以使用包含可以从stdin调用的匿名子例程的哈希。

my %callbacks = (
    add => sub {
        # do stuff
    },
    fuzzerbligh => sub {
        # other stuff
    },
);

您可以在哈希中插入更多哈希值:

$callbacks{next} = sub {
    ...
};

你会调用这样的

$callbacks{next}->(@args);

或者

my $coderef = $callbacks{next};
$coderef->(@args);

您可以从STDIN或其他任何地方获取哈希键。

你也可以不明确地定义它们,然后引用它们。

sub delete {
    # regular sub definition
}

$callbacks{delete} = \&delete;
但是,我不打算称这些回调。回调是在另一个子例程返回后调用的子函数。

您的代码也充斥着语法错误,这可能会掩盖这里的深层问题。我还不清楚你在尝试使用第二级数组做什么。你什么时候定义这些潜艇,谁在什么时候使用它们?是什么?

答案 1 :(得分:8)

也许这个简化的例子会有所帮助:

# Very important.
use strict;
use warnings;

# Define some functions.
sub multiply { $_[0] * $_[1] }
sub divide   { $_[0] / $_[1] }
sub add      { $_[0] + $_[1] }
sub subtract { $_[0] - $_[1] }

# Create a hash of references to those functions (dispatch table).
my %funcs = (
    multiply => \&multiply,
    divide   => \&divide,
    add      => \&add,
    subtract => \&subtract,
);

# Register some more functions.
sub register {
    my ($key, $func) = @_;
    $funcs{$key} = $func;
}

register('+', \&add);    # As above.
register('sum', sub {    # Or using an anonymous subroutine.
    my $s = 0;
    $s += $_ for @_;
    return $s;
});

# Invoke them dynamically.
while (<>){
    my ($op, @args) = split;
    last unless $op and exists $funcs{$op}; # No need for equality tests.
    print $funcs{$op}->(@args), "\n";
}

答案 2 :(得分:5)

关于如何在单个文件中构建调度表并通过它调用函数,您已经得到了一些很好的答案,但您还在讨论是否希望在其他模块中定义函数。如果是这种情况,那么根据每个模块所说的具有可调度功能的动态构建调度表不是更好,而不必担心手动保持它是最新的吗?当然会的!

当然,证明这需要多个文件,我正在使用CPAN的Module::Pluggable来查找提供函数定义的模块。

dispatch_core.pl:

#!/usr/bin/env perl

use strict;
use warnings;

my %dispatch;

use lib '.'; # a demo is easier if I can put modules in the same directory
use Module::Pluggable require => 1, search_path => 'DTable';
for my $plugin (plugins) {
    %dispatch = (%dispatch, $plugin->dispatchable);
}

for my $func (sort keys %dispatch) {
    print "$func:\n";
    $dispatch{$func}->(2, 5);
}

DTable / Add.pm:

package DTable::Add;

use strict;
use warnings;

sub dispatchable {
    return (add => \&add);
}

sub add {
    my ($num1, $num2) = @_;
    print "$num1 + $num2 = ", $num1 + $num2, "\n";
}

1;

DTable / MultDiv.pm:

package DTable::MultDiv;

use strict;
use warnings;

sub dispatchable {
    return (multiply => \&multiply, divide => \&divide);
}

sub multiply {
    my ($num1, $num2) = @_;
    print "$num1 * $num2 = ", $num1 * $num2, "\n";
}

sub divide {
    my ($num1, $num2) = @_;
    print "$num1 / $num2 = ", $num1 / $num2, "\n";
}

1;

然后,在命令行上:

$ ./dispatch_core.pl 
add:
2 + 5 = 7
divide:
2 / 5 = 0.4
multiply:
2 * 5 = 10

添加新功能现在就像使用适当的dispatchable子句将新文件放入DTable目录一样简单。无需再次触摸dispatch_core.pl再添加新功能。

编辑:为了回应评论是否可以在没有Module :: Pluggable的情况下完成这个问题,这里有一个修改过的dispatch_core.pl,它不使用除定义之外的任何外部模块可调度功能:

#!/usr/bin/env perl

use strict;
use warnings;

my %dispatch;

my @dtable = qw(
  DTable::Add
  DTable::MultDiv
);

use lib '.';
for my $plugin (@dtable) { 
    eval "use $plugin";
    %dispatch = (%dispatch, $plugin->dispatchable);
}   

for my $func (sort keys %dispatch) {
    print "$func:\n";
    $dispatch{$func}->(2, 5);
}