2011-09-20 89 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]