在Perl中有更好的方法通过引用传递吗?

时间:2010-03-17 18:06:44

标签: perl parameters pass-by-reference parameter-passing perl-module

我正在做像这样的传递:

use strict;
use warnings;

sub repl {
    local *line = \$_[0]; our $line;
    $line = "new value";
}

sub doRepl {
    my ($replFunc) = @_;
    my $foo = "old value";
    $replFunc->($foo);
    print $foo; # prints "new value";
}

doRepl(\&repl);

有更清洁的方法吗?

原型不起作用,因为我正在使用函数引用(相信我有充分的理由使用函数引用)。

我也不想在$_[0]中的任何地方使用repl因为它很难看。

4 个答案:

答案 0 :(得分:9)

你看过Data::Alias了吗?它允许您使用干净的语法创建词法范围的别名。

您可以使用它来创建像这样的传递引用语义:

use strict;
use warnings;

use Data::Alias;

sub foo {
    alias my ($arg) = @_;
    $arg++;
}

my $count = 0;

foo($count);

print "$count\n";

输出为1,表示对foo的调用修改了其参数。

答案 1 :(得分:3)

有几种方法可以做到这一点。明确地将标量引用传递给$foo,或利用Perl的内置传递引用语义。

明确参考:

my $foo = "old value";
doRepl( \&repl, \$foo );
print $foo; # prints "new value";

sub repl {
    my $line = shift;
    $$line = "new value";
}

sub doRepl {
    my ($replFunc, $foo) = @_;
    $replFunc->($foo);
}

通过引用传递:

my $foo = "old value";
doRepl( \&repl, $foo );
print $foo; # prints "new value";

sub repl {
    $_[0] = "new value";
}

sub doRepl {
    my $replFunc = shift;
    $replFunc->(@_);
}

甚至更喜欢通过参考传递:

my $foo = "old value";
doRepl( \&repl, $foo );
print $foo; # prints "new value";

sub repl {
    $_[0] = "new value";
}

sub doRepl {
    my $replFunc = shift;
    &$replFunc;
}

第一个使用普通的perl硬引用来完成这项工作。

第一次通过ref方法使用Perl将所有函数的参数作为引用传递的事实。调用子例程时,@_的元素实际上是参数列表中值的别名。通过更改$_[0]中的foo(),您实际上会将第一个参数更改为foo()

第二次通过ref方法使用这样一个事实,即使用& sigil调用的sub并没有parens获取其调用者的@_数组。否则它是相同的。

更新:我刚注意到您希望避免$_[0]。如果您愿意,可以在repl中执行此操作:

sub repl {
    for my $line( $_[0] ) {
        $line = 'new value';
    }
}

答案 2 :(得分:3)

sub repl {
    my $line = \$_[0];     # or: my $line = \shift
    $$line = "new value";
}

答案 3 :(得分:0)

我不认为在这种情况下使用local创建别名有什么问题。

动态范围当然是一个强大的功能,但只要您了解副作用(在从作用域调用的函数中可以看到新值,如果同名的词法在范围内,则不能本地化,...)然后它是已经溢出的Perl工具箱的一个有用的补充。

Perl文档中关于local的警告的主要原因是为了防止人们无意中使用它而不是my,以便简化从perl4的过渡。但肯定有local有用的时候,这就是一个。

使用for创建别名也是一种选择,但我发现local的显式语法更清晰。如果考虑性能,它也会快一点。