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

发布于 2024-10-14 06:52:40 字数 709 浏览 6 评论 0原文

我有一个 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;
    }
}

但我收到消息:

在 ./test.pl 第 236 行使用“严格引用”时,无法使用字符串(“/article/”)作为子例程引用。

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

我过去也做过类似的事情,但我通常在没有函数参数的情况下执行此操作,并且不使用模块。

I have a perl script (simplified) like so:

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

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

Essentially, I'm going to loop through %url_map, and if the current URL matches a key, I want to call the function pointed to by the value of that key:

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

But I'm getting the message:

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

Line 236 happens to be the line $url_map{$key}($url, $visits, $idsite);.

I've done similar things in the past, but I'm usually doing it without parameters to the function, and without using a module.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

断爱 2024-10-21 06:52:40

尽管这是一个重复,但这里已经回答了这个问题,所以我不妨发布正确的答案:

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

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

此技术将在哈希中存储代码引用,当使用参数调用时,将依次使用这些参数调用适当的方法。

这个答案忽略了一个重要的考虑因素,那就是确保调用者在方法中正常工作。如果您需要这个,请参阅我上面链接到的问题:

如何获取代码引用构造函数?

Since this is being answered here despite being a dup, I may as well post the right answer:

What you need to do is store a code reference as the values in your hash. To get a code reference to a method, you can use the UNIVERSAL::can method of all objects. However, this is not enough as the method needs to be passed an invocant. So it is clearest to skip ->can and just write it this way:

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

This technique will store code references in the hash that when called with arguments, will in turn call the appropriate methods with those arguments.

This answer omits an important consideration, and that is making sure that caller works correctly in the methods. If you need this, please see the question I linked to above:

How to take code reference to constructor?

谜兔 2024-10-21 06:52:40

你想太多了这个问题。找出两个正斜杠之间的字符串,然后在哈希中查找方法名称(而不是引用)。您可以使用标量变量作为 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;
            }
        }
    }

You're overthinking the problem. Figure out the string between the two forward slashes, then look up the method name (not reference) in a hash. You can use a scalar variable as a method name in Perl; the value becomes the method you actually call:

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

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

Try to get rid of any loops where most of the iterations are useless to you. :)


my previous answer, which I don't like even though it's closer to the problem

You can get a reference to a method on a particular object with can (unless you've implemented it yourself to do otherwise):

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

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

The way you have calls the method and takes a reference to the result. That's not what you want for deferred action.

Now, once you have that, you call it as a normal subroutine dereference, not a method call. It already knows its object:

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;
            }
        }
    }
╭⌒浅淡时光〆 2024-10-21 06:52:40

处理此问题的最佳方法是将方法调用包装在匿名子例程中,您可以稍后调用该子例程。您还可以使用 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;
    }
}

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

The best way to handle this is to wrap your method calls in an anonymous subroutine, which you can invoke later. You can also use the qr operator to store proper regexes to avoid the awkwardness of interpolating patterns into things. For example,

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

Then run through it like this:

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

This approach uses an array of hashes rather than a flat hash, so each regex can be associated with an anonymous sub ref that contains the code to execute. The ->() syntax dereferences the sub ref and invokes it. You can also pass parameters to the sub ref and they'll be visible in @_ within the sub's block. You can use this to invoke the method with parameters if you want.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文