Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/293.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python中的变量作用域问题_Python_Recursion_Call_Callstack - Fatal编程技术网

Python中的变量作用域问题

Python中的变量作用域问题,python,recursion,call,callstack,Python,Recursion,Call,Callstack,我是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

我是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
,对于
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))

变量将返回collatz序列的最终编号,从您输入的任何编号开始。collatz猜想说,由于递归调用堆栈的顺序,在您的示例中,变量
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)

Python中的函数参数是通过值传递的,而不是通过引用传递的。如果将数字传递给函数,该函数将收到该数字的副本。如果函数修改其参数,该更改在函数外部将不可见:

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

x = 10
foo(x)
print("x=", x) # Still 10
在您的情况下,最直接的解决方法是将ctr变成一个全局变量。这非常难看,因为如果你想再次调用collatz函数,你需要将全局值重置回0,但是我展示这个替代方法只是为了证明你的逻辑是正确的,除了通过引用位。(注意collatz函数现在不返回任何内容,答案在全局变量中)

由于Python没有尾部调用优化,如果collatz序列超过1000个步骤(这是Pythons的默认堆栈限制),当前递归代码将因堆栈溢出而崩溃。您可以通过使用循环而不是递归来避免这个问题。这也让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))
如果您想坚持使用递归函数,通常可以避免像您正在尝试的那样使用可变赋值。函数不是修改状态的“子例程”,而是使它们更接近数学函数,数学函数接收值并返回仅依赖于输入的结果。如果这样做的话,对递归进行推理就容易多了。我将把这作为一个练习,但递归函数的典型“框架”是有一个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 #???

预期的结果是什么?您希望通过此实现什么?
ctr=collatz(num//2,ctr)
。您还应该使用Python 3,
/2
是浮点除法,
/2
是整数除法。这是一样的,有什么区别吗?我更改了递归调用,将从递归调用接收的值设置回
ctr
。您编写它的方式是丢弃从递归中得到的值。您应该在回答中解释,不是简单地从数学上给出代码,是的,但这不是此方法返回1的原因。函数应该返回达到1的步数。这与询问者所寻找的不完全一样。他的函数试图计算在达到1之前必须运行多少次。感谢您对递归工作原理的解释。我现在明白多了。我没有很好地解释我想在最终结果中解释什么,所以我将编辑这个问题。@Webtron请检查我的编辑,因为我认为它现在应该解决您的结果。
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 #???