2016-08-11 97 views
3

我是Python的新手,我一直在使用它,但我陷入了一個問題。這裏是我的代碼:Python中的變量作用域問題

def collatz(num,ctr): 
    if(num != 1): 
     ctr+=1 
     if(num%2==0): 
      collatz(num/2,ctr) 
     else: 
      collatz(num*3+1,ctr) 
    return ctr 
test=collatz(9,0) 

對於任何號碼,我把爲num,讓我們說9例如,和0ctrctr永遠是爲1。我使用ctr變量是否錯誤?

編輯: 我想打印多少次函數遞歸。所以ctr將是每個遞歸的計數器。

+0

什麼是預期的結果,你想什麼用這個實現? – harshil9968

+0

'ctr = collat​​z(num // 2,ctr)'。你也應該使用Python 3,而'/ 2'是浮點除法,'// 2'是整數除法。 –

回答

3

的由於遞歸調用堆棧的順序,您示例中的變量ctr將始終爲1。如果返回ctr的一個值,則調用堆棧將開始返回以前的值ctr。基本上,在最後一次遞歸調用中,將返回最高值ctr。但是由於調用堆棧底部的方法調用會返回最後一個值,即將存儲在test,test中的值始終爲1。假設我將參數輸入到collatz中,這會導致該方法的總共五次調用。調用堆棧看起來像這樣下來,

collatz returns ctr --> 5 
collatz returns ctr --> 4 
collatz returns ctr --> 3 
collatz returns ctr --> 2 
collatz returns ctr --> 1 //what matters because ctr is being returned with every method call 

正如你所看到的,無論多少次collatz被調用時,1總是會返回,因爲在調用堆棧底部的呼叫ctr等於1

解決方案可能有很多的東西,但它確實取決於你想要完成的目標,這在你的問題中沒有明確說明。

編輯:如果您想要ctr最終成爲遞歸調用的次數,則只需將ctr分配給方法調用的值。它應該看起來像這樣,

def collatz(num,ctr): 
    if(num != 1): 
     ctr+=1 
     if(num%2==0): 
      ctr = collatz(num/2,ctr) 
     else: 
      ttr = collatz(num*3+1,ctr) 
    return ctr 
test=collatz(9,0) 
+0

感謝您對遞歸如何工作的解釋。我明白現在好多了。我在解釋最終結果時想要的不是很好,所以我會編輯這個問題。 – Webtron

+0

@Webtron檢查我的編輯出來,因爲我認爲它現在應該解決你的結果。 –

+0

非常感謝! – Webtron

3

我改變了你的遞歸調用,將從遞歸調用接收到的值設置爲ctr。你寫它的方式,你丟棄了你從遞歸得到的值。

def collatz(num,ctr): 
    if(num != 1): 
      ctr+=1 
      if(num%2==0): 
        ctr=collatz(num/2,ctr) 
      else: 
        ctr=collatz(num*3+1,ctr) 
    return ctr 

test=collatz(9,0) 
+0

這是一樣的,有什麼區別? – harshil9968

+1

我改變了你的遞歸調用,把從遞歸調用接收到的值設置爲'ctr'。你寫它的方式,你丟棄了你從遞歸得到的值。 –

+2

你應該解釋一下,在答案中,不要簡單地給代碼 –

0

一個例子:

def collatz(number): 

    if number % 2 == 0: 
     print(number // 2) 
     return number // 2 

    elif number % 2 == 1: 
     result = 3 * number + 1 
     print(result) 
     return result 

n = input("Give me a number: ") 
while n != 1: 
    n = collatz(int(n)) 
+0

不完全是提問者正在尋找什麼。他的功能是試圖在數字達到1之前計算它需要運行多少次。 –

-3

變量將返回在Collat​​z序列從你把任何數字開始最終數量考拉茲猜想說,這將永遠是1

+0

數學上,是的,但這不是該方法返回的原因1 –

+0

該函數應該返回*達到的步數* 1. – chepner

0

Python中的函數參數是按值傳遞的,而不是通過引用傳遞的。如果您將某個號碼傳遞給某個功能,該功能會收到該號碼的副本。如果函數修改它的參數,這種變化不會在函數外可見:

def foo(y): 
    y += 1 
    print("y=", y) # prints 11 

x = 10 
foo(x) 
print("x=", x) # Still 10 

在你的情況,最直接的解決方法是使CTR成爲一個全局變量。它非常醜陋,因爲如果要再次調用collat​​z函數,則需要將全局範圍重置爲0,但我只是爲了顯示除了傳遞參考位之外,您的邏輯是正確的。 (請注意collat​​z函數現在不返回任何東西,答案在全局變量中)。

ctr = 0 
def collatz(num): 
    global ctr 
    if(num != 1): 
     ctr+=1 
     if(num%2==0): 
       collatz(num/2) 
     else: 
       collatz(num*3+1) 

ctr = 0 
collatz(9) 
print(ctr) 

因爲Python沒有尾調用優化,當前的遞歸代碼將與堆棧溢出崩潰,如果在Collat​​z序列的長度超過1000步(這是蟒蛇默認堆棧限制)。您可以通過使用循環而不是遞歸來避免此問題。這也可以讓我們擺脫那個麻煩的全局變量。最終的結果是有點更地道的Python,在我看來:

def collats(num): 
    ctr = 0 
    while num != 1: 
     ctr += 1 
     if num % 2 == 0: 
      num = num/2 
     else: 
      num = 3*num + 1 
    return ctr 

print(collatz(9)) 

如果你想堅持用遞歸函數,其一般清潔避免使用可變分配像你正在嘗試做的。函數不是修改狀態的「子程序」,而是使它們更接近數學函數,它接收一個值並返回僅依賴於輸入的結果。如果你這樣做,遞歸推理可能會容易得多。我將離開這個作爲一個練習,但典型的「骨架」的遞歸函數是有一個如果檢查的基本情況和遞歸的情況下聲明:

def collatz(n): 
    if n == 1: 
     return 0 
    else if n % 2 == 0: 
     # tip: something involving collatz(n/2) 
     return #??? 
    else: 
     # tip: something involving collatz(3*n+1) 
     return #???