在Perl中使用变量作为方法名称

时间:2011-01-26 17:33:09

标签: perl perl-module

我有一个perl脚本(简化),如下所示:

my $dh = Stats::Datahandler->new(); ### homebrew module

my %url_map = (
    '/(article|blog)/' => \$dh->articleDataHandler,
    '/video/' => \$dh->nullDataHandler,
); 

基本上,我将循环遍历%url_map,如果当前URL与某个键匹配,我想调用该键值指向的函数:

foreach my $key (keys %url_map) {
    if ($url =~ m{$key}) {
        $url_map{$key}($url, $visits, $idsite);
        $mapped = 1;
        last;
    }
}

但我收到的消息是:

Can't use string ("/article/") as a subroutine ref while "strict refs" in use at ./test.pl line 236.

第236行恰好是第$url_map{$key}($url, $visits, $idsite);行。

我过去做过类似的事情,但我通常在没有函数参数的情况下这样做,并且没有使用模块。

3 个答案:

答案 0 :(得分:5)

因为尽管这是一个双重问题,所以我在这里得到了解答,但我也可以发布正确答案:

您需要做的是将代码引用存储为哈希值中的值。要获取方法的代码引用,可以使用所有对象的UNIVERSAL::can方法。但是,这还不够,因为该方法需要传递一个调用者。因此,最简单的方法是跳过->can并以这种方式写出来:

my %url_map = (
    '/(article|blog)/' => sub {$dh->articleDataHandler(@_)},
    '/video/'          => sub {$dh->nullDataHandler(@_)},
); 

这种技术将代码引用存储在散列中,当使用参数调用时,它将依次使用这些参数调用适当的方法。

这个答案省略了一个重要的考虑因素,即确保caller在方法中正常工作。如果您需要,请参阅我上面链接的问题:

How to take code reference to constructor?

答案 1 :(得分:2)

你是在思考这个问题。找出两个正斜杠之间的字符串,然后在哈希中查找方法名称(不是引用)。您可以在Perl中使用标量变量作为方法名称;该值将成为您实际调用的方法:

 %url_map = (
      'foo' => 'foo_method',
      );

 my( $type ) = $url =~ m|\A/(.*?)/|;
 my $method = $url_map{$type} or die '...';
 $dh->$method( @args );

尝试摆脱大多数迭代对你没用的任何循环。 :)


我以前的回答,我不喜欢,即使它更接近问题

您可以使用can获取对特定对象的方法的引用(除非您自己实现了以外的方法):

my $dh = Stats::Datahandler->new(); ### homebrew module

my %url_map = (
   '/(article|blog)/' => $dh->can( 'articleDataHandler' ),
   '/video/'          => $dh->can( 'nullDataHandler' ),
);

您调用方法并获取结果的引用方式。这不是你想要的推迟行动。

现在,一旦你有了它,你将它称为普通的子程序取消引用,而不是方法调用。它已经知道了它的对象:

BEGIN {
package Foo;

sub new { bless {}, $_[0] }
sub cat { print "cat is $_[0]!\n"; }
sub dog { print "dog is $_[0]!\n"; }
}

my $foo = Foo->new;

my %hash = (
    'cat' => $foo->can( 'cat' ),
    'dog' => $foo->can( 'dog' ),
    );

my @tries = qw( cat dog catbird dogberg dogberry );

foreach my $try ( @tries ) {
    print "Trying $try\n";
    foreach my $key ( keys %hash ) {
    print "\tTrying $key\n";
        if ($try =~ m{$key}) {
            $hash{$key}->($try);
            last;
            }
        }
    }

答案 2 :(得分:0)

处理此问题的最佳方法是将方法调用包装在匿名子例程中,以后可以调用。您还可以使用qr运算符来存储正确的正则表达式,以避免将模式插入到事物中的尴尬。例如,

my @url_map = ( 
    { regex    => qr{/(article|blog)/},
      method   => sub { $dh->articleDataHandler }
    },
    { regex    => qr{/video/},
      method   => sub { $dh->nullDataHandler }
    }
);

然后像这样经历:

foreach my $map( @url_map ) { 
    if ( $url =~ $map->{regex} ) { 
        $map->{method}->();
        $mapped = 1;
        last;
    }
}

此方法使用散列数组而不是平面散列,因此每个正则表达式都可以与包含要执行的代码的匿名子引用相关联。 ->()语法取消引用sub ref并调用它。您还可以将参数传递给子参考,它们将在子块的@_中可见。如果需要,可以使用它来调用带参数的方法。