2011-09-20 73 views
2

原諒我(可能)愚蠢的問題,但我與此代碼(實際上是在一個更大的計劃的東西的模型)亂搞,什麼東西是扔我送行:全局變量自身復位子程序

sub recurse { 
    my $m = shift; 
    $g .= "::" . $m; 
    if ($m == 0) { return $g; } 
    else { $m--; recurse ($m); } 
} 

for ($i = 0; $i < 3; $i++) 
{ 
    my $g = ''; 
    $str = recurse (10); 
    print $str . "\n"; 
} 

'for'循環的第一次迭代正常工作。然而,在後續的迭代中,我遇到了一個問題。如你所見,在遞歸函數被調用之前,全局變量$ g在'for'循環中首先被重置。通過調試器,我可以看到在函數被調用之前,$ g被重新設置爲''。但是,一旦輸入了「遞歸」功能,它就會返回到先前的值。我在這裏錯過了什麼?

作爲一個推論,我不喜歡在這裏使用全局變量。如果沒有將$ g作爲recurse()的參數,那麼「正確」的方法是什麼?

回答

7

my $g是一個局部變量,因此它與recurse內部的變量不同。刪除my將解決這個問題,雖然它仍然是一個難看的代碼。

您可以將$g第二個參數傳遞給resurse函數。

注:use strict;是你的朋友;)

+0

*嘆*我想你是對的。我的懶惰似乎在咬我。我已經修復了代碼,我會在下面發佈它。 –

1

你可以簡單地移動的g定義外循環和函數定義之前。

1

正如yi_H所說,您在循環內聲明的my $grecurse正在使用的$g中的一個完全獨立的變量。它首次運行,因爲所有變量都以undef開頭,它作爲字符串變成空字符串。如果您在撥打recurse後嘗試在循環內打印$g,則會看到它仍然是空的。使用strict有助於捕捉這類錯誤。它可能或可能沒有發現這種情況,具體取決於程序的其他部分。

處理此問題的最簡單方法是將$g作爲參數傳遞。但是,有時使用遞歸閉包更容易。請注意,Perl使用引用計數垃圾回收器,這意味着它不能刪除自引用數據結構(包括引用自身的閉包,以便它可以自我遞歸調用),直到整個程序退出。我使用Scalar::Utilweaken功能來避免這種情況。 $strongRef僅用於在我們完成之前使coderef不被垃圾收集。

use Scalar::Util 'weaken'; 

for (my $i = 0; $i < 3; $i++) 
{ 
    my $g = ''; 

    my $recurse; 
    my $strongRef = $recurse = sub { 
    my $m = shift; 
    $g .= "::" . $m; 
    if ($m == 0) { return $g; } 
    else { $m--; $recurse->($m); } 
    }; 

    weaken($recurse); # Prevent memory leak 

    my $str = $recurse->(10); 
    print $str . "\n"; 
} 

然而,在這種特殊情況下,我可能只是封閉在$str填補返回值的,而不是直接:

for (my $i = 0; $i < 3; $i++) 
{ 
    my $str = ''; 
    my $recurse; 
    my $strongRef = $recurse = sub { 
    my $m = shift; 
    $str .= "::" . $m; 
    if ($m-- > 0) { $recurse->($m); } 
    }; 

    weaken($recurse); # Prevent memory leak 

    $recurse->(10); 
    print $str . "\n"; 
} 
+0

很好的答案。我會把yi_H作爲答案,因爲他首先回答。 –

0

正如那句老話,功夫不負有心人,但懶惰現在支付。下面的代碼是什麼樣子,現在我決定不再懶惰,並做了「正確」的方法(我也改變$ g到一個數組,以避免尷尬的前導分隔符):

use strict; 

sub recurse { 
    my $m = shift; 
    my @g = qw(); 
    @g = @{$_[0]} if $_[0]; 
    push (@g, $m); 
    if ($m == 0) { return @g; } 
    else { $m--; recurse ($m, \@g); } 
} 


for (my $i = 0; $i < 3; $i++) 
{ 
    my @str = recurse (10); 
    print join('::', @str) . "\n"; 
} 
+1

fyi,for語句可能剛好是「for(0..2)」 –

+0

用於避免全局並且沒有接口中第二個變量的技巧是擁有第二個由公共調用的私有工作函數接口功能。例如,子遞歸{work_recurse shift,()} –

0
什麼'正確'的方式來做到這一點,而不需要使用$ ga參數進行遞歸()

簡單,因爲根本不需要$g

sub recurse { 
    my $m = shift; 
    if ($m == 0) { 
    return "::" . $m; 
    } else { 
    return "::" . $m . recurse($m-1); 
    } 
} 

for ($i = 0; $i < 3; $i++) 
{ 
    print recurse(10) . "\n"; 
} 
+0

對於這種情況是正確的,但是$ m表示需要在模型中完成的其他工作。使用這樣的模型代表更難的代碼的副作用之一是很難說出大片的必要部分。 –

+0

@coding_hero,那麼?我沒有改變任何與'$ m'有關的東西。我所做的一切總是返回結果,而不是有時將其添加到全局變量中。如果你可以做到這一點,你可以輕鬆做到另一種。事實上,我敢說,避免全球化實際上簡化了事情。 – ikegami