2011-09-20 85 views
2

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

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循环打印线。这个$ _变量仍然包含传递给该方法的值。

有没有办法强制$ _上的新值范围,还是总是包含原始值?

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

感谢

+1

一般的经验法则是永远不会使用默认标量,除非绝对必要。 'foreach(@results)'与foreach(@results)相比''foreach $ result(@results)'从代码可维护性/可读性来说更好。仅仅因为Perl对字符类型和可读性的折衷非常宽容,并不意味着你不应该总是在可读性方面犯错。总是编码,就好像下一个开发人员维护你的代码是一个狂热的精神病患者,他知道你住在哪里:) – DVK

+0

实际上,这个问题不是'$ _'的范围,它是由于误解Perl命名约定而导致的。 – Wolf

回答

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) {...} 

一般情况下,如果你的代码的长度超过了几行,或嵌套,你应该命名变量,而TH依靠默认的。


由于您的问题涉及多个比范围变量名,我还没有讨论围绕foreach循环的实际范围,但在一般情况下,下面的代码就相当于你有什么。

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

线local *_ = \$results[$i]安装的@results$i的第i个元素到*_水珠,又名$_的标时隙。此时$_包含数组元素的别名。本地化将在循环结束时放松。 local创建一个动态范围,因此在循环内调用的任何子例程将看到新值$_,除非它们也将其本地化。关于这些概念还有更多可用的细节,但我认为它们超出了你的问题的范围。

+0

for循环测试表达式应该是'$ i <= $#results',因为$#results是array @results的最后一个索引。 – dividebyzero

9

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

+2

'$ _-> [0]'比$ {$ _} [0]更清晰' – ikegami

+3

'$$ _ [0]'完全没问题。而且更容易打字。 :) – tchrist

+0

@ikegami,@tchrist,很好,指出。尽管如此,@ @ results'仍然不清楚。 –

2

正如其他人所指出的:

  • 你真的在你的打印语句中使用@_而不是$_
  • 因为它们在其他地方使用,所以将这些变量保留在这些变量中并不好。

正式地,$_@_是全局变量,不是任何包的成员。您可以将范围本地化为my $_,但这可能是一个非常糟糕的主意。问题在于,即使不知道Perl,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]