Perl中默认变量$ _的范围

时间:2011-09-20 19:07:30

标签: perl default perlvar perlsyn

我有以下方法接受变量,然后显示数据库中的信息:

sub showResult {
    if (@_ == 2) {
        my @results = dbGetResults($_[0]);
        if (@results) {
            foreach (@results) {
                print "$count - $_[1] (ID: $_[0])\n";
            }
        } else {
            print "\n\nNo results found";
        }
   }
}

除了foreach循环中的打印行外,一切正常。此$ _变量仍包含传递给方法的值。

无论如何要在$ _上“强制”新的值范围,还是它总是包含原始值?

如果有任何好的教程可以解释$ _的工作范围,那也很酷!

由于

3 个答案:

答案 0 :(得分:9)

这里的问题是你真的使用@_代替$_foreach循环更改$_,即标量变量,而不是@_,如果您按$_[X]对其进行索引,则会更改@results。此外,再次检查代码以查看${$_}[0]内的内容。如果它是数组或引用的数组,您可能需要使用间接{{1}}或类似的东西。

答案 1 :(得分:4)

在Perl中,_名称可以引用许多不同的变量:

常见的是:

$_ the default scalar (set by foreach, map, grep)
@_ the default array  (set by calling a subroutine)

不太常见:

%_ the default hash (not used by anything by default)
 _ the default file handle (used by file test operators)
&_ an unused subroutine name
*_ the glob containing all of the above names

这些变量中的每一个都可以独立使用。事实上,它们相关的唯一方式是它们都包含在*_ glob中。

由于符号因数组和哈希值而异,因此在访问元素时,您可以使用括号字符来确定要访问的变量:

$_[0]   # element of @_
$_{...} # element of %_

$$_[0]  # first element of the array reference stored in $_
$_->[0] # same

for / foreach循环可以接受要使用的变量名称而不是$_,并且在您的情况下可能会更清楚:

for my $result (@results) {...}

通常,如果您的代码长于几行或嵌套,您应该命名变量而不是依赖于默认变量。


由于您的问题更多地与变量名而不是范围相关,我没有讨论foreach循环的实际范围,但一般来说,以下代码与您拥有的相同。

for (my $i = 0; $i < $#results; $i++) {
    local *_ = \$results[$i];
    ...
}

local *_ = \$results[$i]行将$i @results元素安装到*_ glob的标量位,即$_。此时$_包含数组元素的别名。本地化将在循环结束时展开。 local创建一个动态范围,因此从循环内调用的任何子例程都将看到$_的新值,除非它们也将其本地化。有关这些概念的更多详细信息,但我认为它们超出了您的问题的范围。

答案 2 :(得分:2)

正如其他人所指出的那样:

  • 您的打印声明中确实使用了@_而非$_
  • 将这些变量保留在这些变量中是不好的,因为它们在别处使用过。

正式地说,$_@_是全局变量,不是任何包的成员。您可以使用my $_本地化范围,尽管这可能是一个非常非常糟糕的主意。问题是Perl可以在你不知道的情况下使用它们。依靠他们的价值超过几行是不好的做法。

这是你的程序中的一个轻微改写,尽可能地摆脱对@_$_的依赖:

sub showResults {
    my $foo = shift;    #Or some meaningful name
    my $bar = shift;    #Or some meaningful name

    if (not defined $foo) {
       print "didn't pass two parameters\n";
       return;  #No need to hang around
    }
    if (my @results = dbGetResults($foo)) {
        foreach my $item (@results) {
        ...
    }
}

一些修改:

  • 我使用shift为您的两个参数提供实际名称。 foobar不是好名字,但我找不到dbGetResults来自哪个,所以我无法弄清楚你在寻找什么参数。传递参数时仍然使用@_shift取决于@_的值,但在前两行之后,我有空。
  • 由于您的两个参数都有实际名称,因此我可以使用if (not defined $bar)查看是否已传递这两个参数。我也改为负面。这样,如果他们没有传递这两个参数,您可以提前退出。这样,您的代码只有一个缩进,并且您没有占用整个子例程的if结构。它使您更容易理解您的代码。
  • 我使用foreach my $item (@results)代替foreach (@results)并依赖$_。再一次,它更清楚你的程序在做什么,你不会把$_->[0]$_[0]混淆(我认为这就是你在做的事情)。很明显你想要$item->[0]