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

发布于 2024-12-15 21:16:08 字数 1250 浏览 1 评论 0原文

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

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

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

这被一些处理制表符分隔数据的脚本使用;正在检查的列由适当的子程序转换。当处理列中的值时,我将数据的哈希值传递给子函数,它会生成一个标量,即该列的新值。

目前,子程序是这样调用的:

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

并且参数中的变量都声明为“my” - my $data、my $errs 等。是否可以更新传递到子程序的参数中的其他值而无需归还它们?即不必这样做:

 ... 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!

I have a module with a set of functions implemented as a dispatch hash with a helper function thus:

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

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

This is used by some scripts that process tab-delimited data; the column being examined is converted by the appropriate subroutine. When processing a value in a column, I pass a hash of data to the sub, and it generates a scalar, the new value for the column.

Currently the subs are called thus:

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

and the variables in the arguments are all declared as 'my' - my $data, my $errs, etc.. Is it possible to update other values in the arguments that are passed into the subs without having to return them? i.e. instead of having to do this:

 ... 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'

I could do this:

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

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

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

发布评论

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

评论(2

雨轻弹 2024-12-22 21:16:08

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

例如:

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 );

You can pass reference to value and update it inside of subroutine.

For example:

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";

And as far as I can see from your code snippets, $errs is already reference to hash.
So, actually, all you have to do - just comment out line $errs = $return_data->{errs}; and try

If I get your code right, $errs gets updated. And then you should just change your return value to $answer and do:

my $new_value = do_function( 'f1', data => $data, errs => $errs );
陈独秀 2024-12-22 21:16:08
  • 首先将 do_function 的定义更改为:

    sub do_function {
        我的 $fn = 转变;
        转到 &{$functions->{$fn}}
    }
    

    这是分派到新子例程的正确方法。这种形式的 goto 用新的 coderef 替换当前正在执行的子例程,不变地传递 @_ ,并从调用堆栈中删除 do_function (因此 <代码>调用者工作正常)。您可能还需要在其中进行一些错误检查,以确保 $fn 是一个有效的名称。

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

    sub add1 {$_[0]++}
    我的 $x = 1;
    添加1 $x;
    说$x; #2
    

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

    在 $functions->{f1} 中:
      我的%args;
      尽管 (@_) {
          $args{$_} = /错误/? \shift : 换档换档
      }
      ## 处理数据...
      ## 更改 ${$args{errs}}
      ## ...
    
  • HOWEVER 因为在您的情况下 $errs 是一个哈希引用,您不需要需要做任何额外的工作。所有引用都会自动通过引用传递。在现有代码中,您所要做的就是修改 $args{errs} 的键(正如您现在所做的那样),它将修改对该哈希的每个引用。

    如果您想要函数本地哈希,则需要复制哈希*:

    我的%errs = %{$args{errs}};
    

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

    %{$args{errs}} = %errs; # 替换所有键
    @{$args{errs}}{keys %errs} = 值 %errs; # 替换%errs中的键
    ...和 ​​$args{errs}{$_} = $errs{$_} 对于键 %errs; # 条件替换
    

    *或本地化部分/全部键

  • first change your definition of do_function to:

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

    that is the proper way to dispatch to a new subroutine. this form of goto replaces the currently executing subroutine with the new coderef, passing @_ unchanged, and removing do_function from the call stack (so caller works right). you probably want some error checking in there too, to make sure that $fn is a valid name.

  • inside your function, you can simply modify cells of @_ directly, and you do not need to pass anything by reference (since perl already did that for you).

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

    to support key => value arguments without passing by reference you could write it this way:

    in $functions->{f1}:
      my %args;
      while (@_) {
          $args{$_} = /errs/ ? \shift : shift for shift
      }
      ## process data ...
      ## alter ${$args{errs}}
      ## ...
    
  • HOWEVER since in your case $errs is a hash reference, you don't need to do any extra work. all references are passed by reference automatically. in your existing code, all you have to do is modify a key of $args{errs} (as you are doing right now) and it will modify every reference to that hash.

    if you wanted a function local hash, you need to make a copy of the hash*:

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

    where %errs is private, and once you are done, you can push any values you want to make public into $args{errs} with $args{errs}{...} = ...;. but be sure not to replace $args{errs} with the copy (as in $args{errs} = \%errs) since that will break the connection to the caller's error hash. if you want to copy all the new values in, you could use one of:

    %{$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
    

    *or localize some/all of the keys

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