简体   繁体   English

Python中的可变范围问题

[英]Variable Scope Issue in Python

I am new to Python and I have been working with it for a bit, but I am stuck on a problem. 我是Python新手,我一直在使用它,但我遇到了问题。 Here is my code: 这是我的代码:

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)

For any number I put in for num , let's say 9 for instance, and 0 for ctr , ctr always comes out as 1 . 对于我为num输入的任何数字,例如9 ,对于ctr0ctr总是为1 Am I using the ctr variable wrong? 我使用ctr变量错了吗?

EDIT: I am trying to print out how many times the function is recursed. 编辑:我试图打印出函数被递归的次数。 So ctr would be a counter for each recursion. 因此ctr将成为每次递归的计数器。

I changed your recursive calls to set the value received back from the recursive calls into ctr. 我改变了你的递归调用,将从递归调用中收到的值设置为ctr。 The way you wrote it, you were discarding the values you got back from recursing. 你写它的方式,你丢弃了从递归中得到的值。

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)

The variable ctr in your example will always be 1 because of the order of the recursive call stack. 由于递归调用堆栈的顺序,示例中的变量ctr将始终为1 As one value of ctr is returned, then the call stack will start returning the previous values of ctr . 当返回一个ctr值时,调用堆栈将开始返回先前的ctr值。 Basically, at the very last recursive call, the highest value of ctr will be returned. 基本上,在最后一次递归调用时,将返回ctr的最高值。 But since the method call at the bottom of the call stack returns the very last value aka the value that will be stored in test , test will always be 1 . 但是由于调用堆栈底部的方法调用返回最后一个值,即将存储在test的值, test将始终为1 Let's say I input parameters into collatz that would result in five total calls of the method. 假设我将参数输入到collatz ,这将导致该方法的五次调用。 The call stack would look like this coming down, 调用堆栈看起来像这样,

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

As you can see, no matter how many times collatz is called, 1 will always be returned because the call at the bottom of the call stack has ctr equaling 1 . 正如你所看到的,无论有多少次collatz叫, 1将始终被返回,因为在调用堆栈底部的调用有ctr等于1

The solution can be a lot of things, but it really depends on the purpose of what you're trying to accomplish which isn't clearly stated in your question. 解决方案可以是很多东西,但它实际上取决于你想要完成的目的,这在你的问题中没有明确说明。

EDIT: If you want ctr to end up being the number of times a recursive call is made, then just assign ctr to the value of the method call. 编辑:如果您希望ctr最终成为递归调用的次数,那么只需将ctr分配给方法调用的值。 It should look like this, 看起来应该是这样的,

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)

An example: 一个例子:

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))

Function parameters in Python are passed by value, not by reference. Python中的函数参数是按值传递的,而不是通过引用传递的。 If you pass a number to a function, the function receives a copy of that number. 如果将数字传递给函数,函数将接收该数字的副本。 If the function modifies its parameter, that change will not be visible outside the function: 如果函数修改了其参数,则该更改将在函数外部不可见:

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

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

In your case, the most direct fix is to make ctr into a global variable. 在您的情况下,最直接的解决方法是将ctr变为全局变量。 Its very ugly because you need to reset the global back to 0 if you want to call the collatz function again but I'm showing this alternative just to show that your logic is correct except for the pass-by-reference bit. 它非常难看,因为如果你想再次调用collat​​z函数你需要将全局重置为0,但是我只是为了表明你的逻辑是正确的,除了传递引用位之外。 (Note that the collatz function doesn't return anything now, the answer is in the global variable). (注意,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)

Since Python doesn't have tail-call-optimization, your current recursive code will crash with a stack overflow if the collatz sequence is longer than 1000 steps (this is Pythons default stack limit). 由于Python没有尾调用优化,如果collat​​z序列长度超过1000步,那么当前的递归代码将因堆栈溢出而崩溃(这是Pythons的默认堆栈限制)。 You can avoid this problem by using a loop instead of recursion. 您可以通过使用循环而不是递归来避免此问题。 This also lets use get rid of that troublesome global variable. 这也可以让我们摆脱那个麻烦的全局变量。 The final result is a bit more idiomatic Python, in my opinion: 在我看来,最终的结果是更具惯用性的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 you want to stick with using recursive functions, its usually cleaner to avoid using mutable assignment like you are trying to do. 如果你想坚持使用递归函数,它通常更干净,以避免使用像你试图做的可变赋值。 Instead of functions being "subroutines" that modify state, make them into something closer to mathematical functions, which receive a value and return a result that depends only on the inputs. 而不是函数是修改状态的“子程序”,而是将它们变成更接近数学函数的东西,它接收一个值并返回仅依赖于输入的结果。 It can be much easier to reason about recursion if you do this. 如果你这样做,可以更容易推理递归。 I will leave this as an exercise but the typical "skeleton" of a recursive function is to have an if statement that checks for the base case and the recursive cases: 我将此作为练习,但递归函数的典型“骨架”是使用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 #???

变量将从你输入的任何数字开始返回最后一个collat​​z序列号.colltz猜想说这将永远是1

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM