Perl:调度哈希值和共享变量

时间:2011-11-15 17:58:34

标签: perl variables scope anonymous-function

我有一个带有一组函数的模块,这些函数实现为带有辅助函数的调度哈希,因此:

my $functions = {
  'f1' => sub { 
      my %args = @_;
      ## process data ...
      return $answer; 
  },
[etc.]
};

sub do_function {
    my $fn = shift;
    return $functions->{$fn}(@_);
}

一些处理制表符分隔数据的脚本使用它;正在检查的列由适当的子例程转换。在处理列中的值时,我将数据散列传递给sub,并生成标量,即列的新值。

目前,这些潜艇被称为:

my $new_value = do_function( 'f1', data => $data, errs => $errs );

并且参数中的变量都被声明为'my' - 我的$ data,我的$ errs等等。是否有可能更新传递给subs的参数中的其他值而不必返回它们?即不必这样做:

 ... in $functions->{f1}:
      my %args = @_;
      ## process data ...
      ## alter $args{errs}
      $args{errs}->{type_one_error}++; 
      ## ...
      return { answer => $answer, errs => $args{errs} }; 
 ...

 ## call the function, get the response, update the errs
 my $return_data = do_function( 'f1', data => $data, errs => $errs );
 my $new_value = $return_data->{answer};
 $errs = $return_data->{errs}; ## this has been altered by sub 'f1'

我可以这样做:

  my $new_value = do_function( 'f1', data => $data, errs => $errs );
  ## no need to update $errs, it has been magically updated already!

2 个答案:

答案 0 :(得分:3)

您可以传递对值的引用并在子例程内更新它。

例如:

sub update {
    my ($ref_to_value) = @_;
    $$ref_to_value = "New message";
    return "Ok";
}

my $message = "Old message";

my $retval = update(\$message);

print "Return value: '$retval'\nMessage: '$message'\n";

就我在代码片段中看到的而言,$errs已经引用了哈希。 所以,实际上,您只需要注释掉$errs = $return_data->{errs};行并尝试

如果我的代码正确,$errs会更新。然后您应该将返回值更改为$answer并执行:

my $new_value = do_function( 'f1', data => $data, errs => $errs );

答案 1 :(得分:1)

  • 首先将do_function的定义更改为:

    sub do_function {
        my $fn = shift;
        goto &{$functions->{$fn}}
    }
    

    这是分派到新子程序的正确方法。这种形式的goto用新的coderef替换当前正在执行的子例程,不更改@_,并从调用堆栈中删除do_function(因此caller工作正常)。您可能也希望在那里进行一些错误检查,以确保$fn是有效名称。

  • 在你的函数中,你可以直接修改@_的单元格,你不需要通过引用传递任何东西(因为perl已经为你做了)。

    sub add1 {$_[0]++}
    my $x = 1;
    add1 $x;
    say $x; # 2
    

    支持key => value参数而不通过引用传递,你可以这样写:

    in $functions->{f1}:
      my %args;
      while (@_) {
          $args{$_} = /errs/ ? \shift : shift for shift
      }
      ## process data ...
      ## alter ${$args{errs}}
      ## ...
    
  • HOWEVER ,因为在您的情况下$errs是哈希引用,您不需要做任何额外的工作。所有引用都自动通过引用传递。在现有代码中,您所要做的就是修改$args{errs}的密钥(正如您现在所做的那样),它将修改对该哈希的每个引用。

    如果你想要一个函数本地哈希,你需要复制哈希*:

    my %errs = %{$args{errs}};
    

    其中%errs是私有的,完成后,您可以使用$args{errs}将要公开的任何值推送到$args{errs}{...} = ...;。但请确保不要将$args{errs}替换为副本(如$args{errs} = \%errs中所示),因为这会破坏与调用方错误哈希的连接。如果要复制所有新值,可以使用以下方法之一:

    %{$args{errs}} = %errs;                             # replace all keys
    @{$args{errs}}{keys %errs} = values %errs;          # replace keys in %errs
    ... and $args{errs}{$_} = $errs{$_} for keys %errs; # conditional replace
    

    *或本地化一些/所有键