【问题标题】:Is there a better way to pass by reference in Perl?有没有更好的方法在 Perl 中通过引用传递?
【发布时间】:2011-01-28 17:05:37
【问题描述】:

我正在做这样的传递引用:

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

有没有更清洁的方法?

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

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

【问题讨论】:

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


    【解决方案1】:

    我认为在这种情况下使用local 创建别名没有任何问题。

    动态范围当然是一个强大的功能,但只要您意识到副作用(新值在从其范围调用的函数中可见,如果同名词汇在范围内,它就不能被本地化,...) 那么它是对已经溢出的 Perl 工具箱的有用补充。

    Perl 文档中关于 local 的警告的主要原因是防止人们无意中使用它而不是 my 并简化从 perl4 的过渡。但是local 肯定会有用处,这就是其中之一。

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

    【讨论】:

      【解决方案2】:

      有几种方法可以做到这一点。将标量 ref 显式传递给 $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 将参数作为引用传递给所有函数的事实。 @_ 的元素实际上是调用子例程时参数列表中的值的别名。通过在foo() 中更改$_[0],实际上您将第一个参数更改为foo()

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

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

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

      【讨论】:

        【解决方案3】:

         

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

        【讨论】:

        • @Sinan - 没想到会起作用。每天学习新东西。
        • 规则参见perldoc.perl.org/perlref.html#Making-References "作为一种特殊情况,\(@foo) 返回对@foo 内容的引用列表,而不是对@foo 本身的引用。"
        • 在标量上下文中,\(@foo) 是对包含@foo 中元素数量的标量值的引用[即,如\(scalar @foo)]
        【解决方案4】:

        你看过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的调用修改了它的参数。

        【讨论】:

        • 不幸的是,我查看了文档的“实施”部分...... ack!我想这个模块有点像香肠……如果你不知道它们是怎么做的,那就更有趣了……:)
        • @JoelFan:是的。这是一个非常“不要在家尝试”的模块:)
        • 从“这个模块不使用源过滤器”开始,然后从那里走下坡路...... :)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-14
        • 2013-11-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-24
        相关资源
        最近更新 更多