我有以下方法接受变量,然后显示数据库中的信息:
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循环中的打印行外,一切正常。此$ _变量仍包含传递给方法的值。
无论如何要在$ _上“强制”新的值范围,还是它总是包含原始值?
如果有任何好的教程可以解释$ _的工作范围,那也很酷!
由于
答案 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
为您的两个参数提供实际名称。 foo
和bar
不是好名字,但我找不到dbGetResults
来自哪个,所以我无法弄清楚你在寻找什么参数。传递参数时仍然使用@_
,shift
取决于@_
的值,但在前两行之后,我有空。if (not defined $bar)
查看是否已传递这两个参数。我也改为负面。这样,如果他们没有传递这两个参数,您可以提前退出。这样,您的代码只有一个缩进,并且您没有占用整个子例程的if结构。它使您更容易理解您的代码。foreach my $item (@results)
代替foreach (@results)
并依赖$_
。再一次,它更清楚你的程序在做什么,你不会把$_->[0]
与$_[0]
混淆(我认为这就是你在做的事情)。很明显你想要$item->[0]
。