如何在 Perl 哈希中存储和使用变量和子例程名称?

hw_*_*_sw 2 perl

我的程序处理两种风格的“某物”,每种风格都有自己的数据结构和程序来处理它们。用户使用以下任一或两者调用程序:

  • -f1 path_to_file_with_flavor_1_data
  • -f2 path_to_file_with_flavor_2_data

我的程序工作编码为:

GetOptions ('f1=s' => \$f1_path,
            'f2=s' => \$f2_path,
           );

if (defined $f1_path) {
  subroutine_to_process_flavor_1_data( $f1_path );
}
if (defined $f2_path) {
  subroutine_to_process_flavor_2_data( $f2_path );
}
Run Code Online (Sandbox Code Playgroud)

它有一个散列来存储两种口味的处理数据:

my %flv_hash = ( flavor_1 => { datahash => { ... },
                             },
                 flavor_2 => { datahash => { ... },
                             },
               );
Run Code Online (Sandbox Code Playgroud)

我现在想将每个风味的变量和子程序名称添加到哈希中以使其:

my %flv_hash = ( flavor_1 => { datahash => { ... },
                               var_name => 'f1_path',
                               sub_name => 'subroutine_to_process_flavor_1_data',
                              },
                 flavor_2 => { datahash => { ... },
                               var_name => 'f2_path',
                               sub_name => 'subroutine_to_process_flavor_2_data',
                              },
               );
Run Code Online (Sandbox Code Playgroud)

并将我的程序更改为以下伪代码:

foreach my $flavor ( keys %flv_hash ) {
   if (defined <the variable named $flv_hash{flavor}{var_name}>) {
      <call the subroutine named $flv_hash{flavor}{sub_name}>
   }
}
Run Code Online (Sandbox Code Playgroud)

我已经搜索了所有关于在哈希中存储和检索变量和子例程的名称的知识库,但是,作为一名硬件工程师,他的软件技能仅限于我在 35 年前在基本编程 101 中学到的知识,我无法直接复制示例并使它们在我的程序上下文中工作。换句话说,如果可能的话,我会很感激我可以复制和使用的解决方案,而无需深入了解它们所基于的 Perl 范式。再次感谢你。

zdi*_*dim 6

在那个漂亮的布局中的一件事是使用names,对于子例程和变量,在形成散列时 - 你实际上不能使用这些东西来运行子例程或评估变量,只是他们的裸名。

相反,您可以采用一个子程序引用,它是一个标量,因此可以是一个哈希值,然后可以通过取消引用来执行子程序;并使用变量作为值。

哈希值

my %flv_hash = ( 
    flavor_1 => { 
        data => { ... },
        var  => $f1_path,
        code => \&subroutine_to_process_flavor_1_data,
    },
    flavor_2 => { 
        data => { ... },
        var  => $f2_path,
        code => \&subroutine_to_process_flavor_2_data,
    },
);
Run Code Online (Sandbox Code Playgroud)

code对于键而言,它只是一个更好名称的占位符)。按照指示使用它

foreach my $flavor ( keys %flv_hash ) {
   if (defined $flv_hash{$flavor}{var}) {
      $flv_hash{$flavor}{code}->( $flv_hash{$flavor}{var} );
   }
}
Run Code Online (Sandbox Code Playgroud)

\&sub_name语法接受并返回对子例程引用,因此是一个标量,它可以像任何其他标量一样分配和/或操作。

创建此类代码引用的另一种方法是使用匿名子例程,通过使用语法直接分配子例程代码。

my $code_reference = sub { subroutine-code };
Run Code Online (Sandbox Code Playgroud)

code => sub { ... }如果 subs 又短又甜,你还可以在 hash ( ) 中做什么。

然后符号$coderef->( LIST )是我们如何执行子程序及其在标量变量中的引用$coderef。如果没有参数,我们需要空括号。

一旦采用代码引用,当然有多种组织方式;我只是遵循问题的合理意图。在这方面可能有用的另一个项目是调度表;最近的一个参考是在这个页面上,还有更多。

另请参见例如这篇文章,以及来自The Effective Perler ... 等的更多文章


我突然想到,也许还需要子名称本身。

从代码引用中检索名称的一种简单方法是使用Sub::Util

use Sub::Util qw(subname);

say subname( $coderef );
Run Code Online (Sandbox Code Playgroud)

这是核心,因为我认为是 perl-5.22.0 (?)。CPAN 上还有Sub::Identify

然后,全能的B给出了更多 via svref_2oject($coderef),用于代码引用返回一个B::CV对象,在该对象上也可以使用B::GV方法

use B qw(svref_2object);

my $cv = svref_2oject($coderef);

say for $cv->FILE, $cv->GV->NAME;  # etc
Run Code Online (Sandbox Code Playgroud)