Python 中的变量范围问题
Variable Scope Issue in 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
,0
为 ctr
,ctr
总是出现1
。我使用的 ctr
变量错了吗?
编辑:
我试图打印出函数被递归的次数。所以 ctr
将是每个递归的计数器。
我更改了您的递归调用,将从递归调用返回的值设置为 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)
一个例子:
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))
该变量将 return 从您输入的任何数字开始的 collatz 序列的最终数字。collatz 猜想说这将始终为 1
由于递归调用堆栈的顺序,您示例中的变量 ctr
将始终为 1
。当返回 ctr
的一个值时,调用堆栈将开始返回 ctr
的先前值。基本上,在最后一次递归调用时,将返回 ctr
的最大值。但是由于调用堆栈底部的方法调用 returns 最后一个值,也就是将存储在 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)
Python 中的函数参数按值传递,而不是按引用传递。如果将数字传递给函数,函数会收到该数字的副本。如果函数修改了它的参数,则该更改在函数外将不可见:
def foo(y):
y += 1
print("y=", y) # prints 11
x = 10
foo(x)
print("x=", x) # Still 10
对于您的情况,最直接的解决方法是将 ctr 设为全局变量。它非常丑陋,因为如果你想再次调用 collatz 函数,你需要将全局重置回 0,但我展示这个替代方案只是为了表明你的逻辑是正确的,除了 pass-by-reference 位。 (请注意,collatz 函数现在没有 return 任何东西,答案在全局变量中)。
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 没有尾调用优化,如果 collatz 序列超过 1000 步(这是 Python 的默认值,您当前的递归代码将因堆栈溢出而崩溃堆栈限制)。您可以通过使用循环而不是递归来避免此问题。这也让 use 摆脱了那个麻烦的全局变量。最后的结果比较地道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))
如果您想坚持使用递归函数,通常更清晰地避免像您尝试那样使用可变赋值。不是 "subroutines" 修改状态的函数,而是使它们更接近数学函数,它接收一个值和 return 一个仅取决于输入的结果。如果你这样做,关于递归的推理会容易得多。我将把它留作练习,但是递归函数的典型 "skeleton" 是有一个 if 语句来检查基本情况和递归情况:
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 #???
我是 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
,0
为 ctr
,ctr
总是出现1
。我使用的 ctr
变量错了吗?
编辑:
我试图打印出函数被递归的次数。所以 ctr
将是每个递归的计数器。
我更改了您的递归调用,将从递归调用返回的值设置为 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)
一个例子:
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))
该变量将 return 从您输入的任何数字开始的 collatz 序列的最终数字。collatz 猜想说这将始终为 1
由于递归调用堆栈的顺序,您示例中的变量 ctr
将始终为 1
。当返回 ctr
的一个值时,调用堆栈将开始返回 ctr
的先前值。基本上,在最后一次递归调用时,将返回 ctr
的最大值。但是由于调用堆栈底部的方法调用 returns 最后一个值,也就是将存储在 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)
Python 中的函数参数按值传递,而不是按引用传递。如果将数字传递给函数,函数会收到该数字的副本。如果函数修改了它的参数,则该更改在函数外将不可见:
def foo(y):
y += 1
print("y=", y) # prints 11
x = 10
foo(x)
print("x=", x) # Still 10
对于您的情况,最直接的解决方法是将 ctr 设为全局变量。它非常丑陋,因为如果你想再次调用 collatz 函数,你需要将全局重置回 0,但我展示这个替代方案只是为了表明你的逻辑是正确的,除了 pass-by-reference 位。 (请注意,collatz 函数现在没有 return 任何东西,答案在全局变量中)。
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 没有尾调用优化,如果 collatz 序列超过 1000 步(这是 Python 的默认值,您当前的递归代码将因堆栈溢出而崩溃堆栈限制)。您可以通过使用循环而不是递归来避免此问题。这也让 use 摆脱了那个麻烦的全局变量。最后的结果比较地道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))
如果您想坚持使用递归函数,通常更清晰地避免像您尝试那样使用可变赋值。不是 "subroutines" 修改状态的函数,而是使它们更接近数学函数,它接收一个值和 return 一个仅取决于输入的结果。如果你这样做,关于递归的推理会容易得多。我将把它留作练习,但是递归函数的典型 "skeleton" 是有一个 if 语句来检查基本情况和递归情况:
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 #???