我有一个带有一组函数的模块,这些函数实现为带有辅助函数的调度哈希,因此:
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!
答案 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
*或本地化一些/所有键