【问题标题】:Should I use $_[0] or copy the argument list in Perl?我应该使用 $_[0] 还是复制 Perl 中的参数列表?
【发布时间】:2009-11-18 18:02:48
【问题描述】:

如果我将哈希传递给子:

parse(\%data);

我应该首先使用$_[0] 的变量,还是可以在我想从哈希中获取元素时继续访问$_[0]?澄清:

sub parse
{    $var1 = $_[0]->{'elem1'};
     $var2 = $_[0]->{'elem2'};
     $var3 = $_[0]->{'elem3'};
     $var4 = $_[0]->{'elem4'};
     $var5 = $_[0]->{'elem5'};
}
# Versus
sub parse
{    my $hr = $_[0];
     $var1 = $hr->{'elem1'};
     $var2 = $hr->{'elem2'};
     $var3 = $hr->{'elem3'};
     $var4 = $hr->{'elem4'};
     $var5 = $hr->{'elem5'};
}

第二个版本是否更正确,因为它不必一直访问参数数组,还是 Perl 最终会以同样的方式对它们进行交互?

【问题讨论】:

    标签: perl hash


    【解决方案1】:

    在这种情况下没有区别,因为您传递的是对哈希的引用。但是在传递标量的情况下会有区别:

    sub rtrim {
        ## remove tailing spaces from first argument
        $_[0] =~ s/\s+$//;
    }
    rtrim($str); ## value of the variable will be changed
    
    sub rtrim_bugged {
        my $str = $_[0]; ## this makes a copy of variable
        $str =~ s/\s+$//;
    }
    rtrim($str); ## value of the variable will stay the same
    

    如果您要传递哈希引用,则只会创建引用的副本。但是哈希本身是一样的。因此,如果您关心代码可读性,那么我建议您为所有参数创建一个变量。例如:

    sub parse {
         ## you can easily add new parameters to this function
         my ($hr) = @_; 
    
         my $var1 = $hr->{'elem1'};
         my $var2 = $hr->{'elem2'};
         my $var3 = $hr->{'elem3'};
         my $var4 = $hr->{'elem4'};
         my $var5 = $hr->{'elem5'};
    }
    

    此外,更具描述性的变量名称也会改进您的代码。

    【讨论】:

      【解决方案2】:

      关于shift 与直接访问@_ 的效率的一般讨论,请参阅:

      至于您的具体代码,我会使用shift,但使用哈希切片简化数据提取:

      sub parse
      {
          my $hr = shift;
          my ($var1, $var2, $var3, $var4, $var5) = @{$hr}{qw(elem1 elem2 elem3 elem4 elem5)};
      }
      

      我假设此方法对这些变量做了其他事情,因此值得将它们保存在单独的变量中(也许哈希是只读的,您需要在将它们插入其他数据之前进行一些修改? ) -- 否则为什么不把它们留在它们开始的 hashref 中呢?

      【讨论】:

      • 将项目放入变量的一个很好的理由是strict 'vars' 提供的错字保护。您还可以通过使用Hash::Util::lock_keys 锁定哈希键获得同样的好处。
      • 这根本不是我在我的功能中真正做的事情,我只是在问题中举了一个例子。
      • @OP:好的,我就是这么想的。 :) (PS。你可以去你的个人资料页面给自己一个更好的用户名 - stackoverflow.com/users/105033
      • 除了一个例外,我更愿意避免移动@_,而是从它做一个列表分配——即使是一个参数,因为这意味着我不会通过不小心转动@来引入错误987654331@ 转为my ($one, $two) = shift。 OO 代码中的例外情况(尤其是构造函数,或委托或重新分配的函数),其中转移 $self 会使事情变得更简单。
      【解决方案3】:

      您正在进行微优化;尽量避免这种情况。选择最易读/可维护的东西。通常这将是您使用词法变量的地方,因为它的名称表明了它的用途……但如果您使用像 $data$x 这样的名称,这显然不适用。

      就技术细节而言,对于大多数目的,您可以通过计算 perl 将使用的基本操作数来估计所花费的时间。对于您的$_[0],非词法数组变量中的元素查找需要多个操作:一个获取 glob,一个获取 glob 的数组部分,一个或多个获取索引(仅一个用于常量) , 和一个查找元素。 $hr,另一方面是一个单一的操作。为了迎合@_ 的直接用户,有一个优化可以将$_[0] 的操作减少为单个组合操作(当索引介于 0 和 255 之间时),但在您的情况下不使用它,因为哈希- deref context 需要在数组元素查找上附加一个标志(以支持自动激活),并且优化的操作不支持该标志。

      总而言之,使用词汇会更易读,并且(如果您多次使用它)会更快。

      【讨论】:

        【解决方案4】:

        我的规则是我尽量不要在长于几个语句的子例程中使用$_[0]。之后,一切都得到一个用户定义的变量。

        为什么要将所有哈希值复制到变量中?只需将它们留在它们所属的哈希中即可。这比您正在考虑的优化要好得多。

        【讨论】:

        • 我只在两种情况下使用$_[0]:类访问器(符合您的“一对语句”规则)以及在极少数情况下我想修改参数并需要保留别名。
        • @Michael Carman:如果你愿意,你可以避免使用$_[0],并像这样明确记录你的就地修改:perl -Mstrict -wle'sub arg_aliases { \@_ } sub foo { my $arg_aliases = &arg_aliases; my ($foo) = @_; print "foo was $foo"; $arg_aliases->[0] = "plugh" } my $foo = "xyzzy"; foo($foo); print "foo is now $foo"'
        • @ysth,你就不能把 refs 带入 @_ 吗?像sub foo { my $arg1 = \$_[0]; $$arg1 =~ s/foo/bar/; } 这样的东西可以避免额外的函数调用。或者要获取整个数组,my @arg_refs = map {\$_} @_;
        【解决方案5】:

        虽然第二个更清楚,但还是一样

        【讨论】:

          【解决方案6】:

          既然可以工作,两者都可以,一般做法是shift关闭参数。

          sub parse { my $hr = shift; my $var1 = $hr->{'elem1'}; }
          

          【讨论】:

          • 我不会说“这是常见的做法”。这实际上是一场仅限 Perl 的圣战。
          • 我的 ($one,$two) = ((shift),(shift));或我的 ($one,$two) = @_; ?
          • $hr->{elem1}$$hr{elem1} 很时髦。 $$hr->{elem1} 不起作用。
          • 我每次使用 shift 时都想扇自己耳光。这是一个非常糟糕的习惯,我几乎不想改变@_。
          • 好的,“澄清”时间:班次对于一个变量来说非常有用。 my ($a, $b, $c) = @_ 非常适合多个变量。很抱歉有任何混淆。
          猜你喜欢
          • 2015-02-12
          • 2023-03-02
          • 1970-01-01
          • 2010-10-27
          • 1970-01-01
          • 2013-07-12
          • 1970-01-01
          • 2013-01-30
          • 2011-08-08
          相关资源
          最近更新 更多