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

qod*_*nja 3 perl hash function 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{
...
}
Run Code Online (Sandbox Code Playgroud)

一个警告是sub在其他模块中定义,所以回调必须能够引用它们...而且我很难让哈希正确!

mas*_*onk 11

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

my %callbacks = (
    add => sub {
        # do stuff
    },
    fuzzerbligh => sub {
        # other stuff
    },
);
Run Code Online (Sandbox Code Playgroud)

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

$callbacks{next} = sub {
    ...
};
Run Code Online (Sandbox Code Playgroud)

你会调用这样一个

$callbacks{next}->(@args);
Run Code Online (Sandbox Code Playgroud)

要么

my $coderef = $callbacks{next};
$coderef->(@args);
Run Code Online (Sandbox Code Playgroud)

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

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

sub delete {
    # regular sub definition
}

$callbacks{delete} = \&delete;
Run Code Online (Sandbox Code Playgroud)

但是,我不打算称这些回调.回调是在另一个子例程返回后调用的子函数.

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

  • 它们被称为[发送表](http://en.wikipedia.org/wiki/Dispatch_table). (5认同)
  • 重新"你的例子中缺少一个箭头",没有箭头是可选的.`perl -wE"$ x {f} = sub {say'hi'}; $ x {f}()"` (2认同)

FMc*_*FMc 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";
}
Run Code Online (Sandbox Code Playgroud)


Dav*_*man 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);
}
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)

然后,在命令行上:

$ ./dispatch_core.pl 
add:
2 + 5 = 7
divide:
2 / 5 = 0.4
multiply:
2 * 5 = 10
Run Code Online (Sandbox Code Playgroud)

现在,添加新功能就像将一个带有适当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);
}   
Run Code Online (Sandbox Code Playgroud)