【问题标题】:How can I loop through a list of functions in Perl?如何遍历 Perl 中的函数列表?
【发布时间】:2009-08-14 03:11:33
【问题描述】:

我有一个 Perl 中的函数列表。示例:

my @funcs = qw (a b c)

现在它们都属于这个模块 Foo::Bar::Stix。我想在循环中迭代地调用它们:

foreach $func (@funcs) {
    Foo::Bar::Stix::$func->(%args)
}

其中 args 是参数的散列。但是我不断收到此错误:“Bad name after :: ...”在包含 Foo::Bar::Stix::$func->(%args) 的行中如何解决此错误?

a b 和 c 不是函数对象而是字符串

【问题讨论】:

    标签: perl function loops


    【解决方案1】:

    与其将函数的名称存储在数组中,不如将对它们的引用存储在散列中,以便您可以按名称引用它们。下面是一个简单的代码示例:

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    my %func_refs = (
        'a' => \&Foo::Bar::Stix::a,
        'b' => \&Foo::Bar::Stix::b,
        'c' => \&Foo::Bar::Stix::c
    );
    
    foreach my $func_ref ( values %func_refs ) {
        print $func_ref->( "woohoo: " ), "\n";
    }
    
    {
      package Foo::Bar::Stix;
    
      sub a {
        my $arg = shift;
        return $arg . "a";
      }
    
      sub b {
        my $arg = shift;
        return $arg . "b";
      }
    
      sub c {
        my $arg = shift;
        return $arg . "c";
      }
    }
    

    如果您因某种原因无法存储名称,请尝试以下操作:

    my $package    = "Foo::Bar::Stix";
    my @func_names = qw/ a b c /;
    foreach my $func_name (@func_names) {
        my $str = &{ "$package\::$func_name" }( "woohoo: " );
        print $str, "\n";
    }
    

    但是,这在use strict 下不起作用,因此我更喜欢第一种解决方案。无论你做什么,尽量避免使用 eval。这是不必要的,并且可能只会给您带来问题。

    此外,大多数使用 Perl 的人将其大写为 Perl 而不是 PERL。这是关于该主题的 Stackoverflow 问题:

    How should I capitalize Perl?

    【讨论】:

    • +1 用于链接到大写问题。如果您将 Perl 视为 PERL,您的业力将如此糟糕,以至于任何事情都不会起作用。
    • $Foo::Bar::Stix::{$func}->(%args); 也可以,并且不需要关闭 strict。
    【解决方案2】:

    错误答案:使用符号引用:

    for $func (@funcs) {
        &{"Foo::Bar::Stix::$func"}(\%args);
    }
    

    好答案:使用调度表:

    my %call_func = (
        'a' => \&Foo::Bar::Stix::a,
        'b' => \&Foo::Bar::Stix::b,
        'c' => \&Foo::Bar::Stix::c,
    );
    ...
    for $func (@funcs) {
        $call_func{$func}->(\%args);
    }
    

    【讨论】:

    • 我完全同意你的看法,我的回答也是这么说的。
    【解决方案3】:

    语法稍有改变就会得到你想要的

    Foo::Bar::Stix->$func(%args)
    

    虽然这会将包名作为第一个参数传递。

    【讨论】:

      【解决方案4】:

      您可以使用can

      my @funcs = qw (a b c)
      foreach $func (@funcs) {
          Foo::Bar::Stix->can($func)->(%args)
      }
      

      【讨论】:

      • 我更喜欢在Stack Overflow上使用qw' a b c'qw" a b c ",这样高亮更合理。
      【解决方案5】:

      您可以通过特殊的%Foo::Bar::Stix:: 变量访问它。这可以直接完全访问符号表。您还会注意到它在严格模式下工作。

      #! /usr/bin/env perl
      use strict;
      use warnings;
      
      {
        package Foo::Bar::Stix;
        sub a{ print "sub a\n" }
        sub b{ print "sub b\n" }
        sub c{ print "sub c\n" }
      }
      
      my @funcs = qw' a b c ';
      my %args;
      
      for my $func (@funcs) {
        $Foo::Bar::Stix::{$func}->(%args); # <====
      }
      

      另一种选择:

      my $symbol_table = $::{'Foo::'}{'Bar::'}{'Stix::'};
      
      my %funcs = (
        # we only want the CODE references
        'a' => *{ $symbol_table->{'a'} }{'CODE'},
        'b' => *{ $symbol_table->{'b'} }{'CODE'},
        'c' => *{ $symbol_table->{'c'} }{'CODE'},
      );
      
      for my $func (@funcs) {
        $funcs{$func}->(%args); # <====
      }
      

      如果您要为大量子例程执行此操作,这就是我加载%funcs 变量的方式。

      my %funcs;
      BEGIN{
        my $symbol_table = $::{'Foo::'}{'Bar::'}{'Stix::'};
      
        for my $name (qw' a b c '){
          $funcs{$name} = *{ $symbol_table->{$name} }{'CODE'};
        }
      }
      

      我不会这样做,除非您需要子例程同时具有完全限定名称,并通过散列变量访问它。

      如果您只需要通过散列变量访问子例程,这是一种更好的设置方法。

      my %funcs = (
        'a' => sub{ print "sub a\n" },
        'b' => sub{ print "sub b\n" },
        'c' => sub{ print "sub c\n" },
      );
      

      注意:您可以将“my %funcs”替换为“our %funcs

      【讨论】:

        猜你喜欢
        • 2011-12-23
        • 1970-01-01
        • 2015-03-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-03-06
        • 2019-07-22
        • 2016-08-03
        相关资源
        最近更新 更多