Variable Scope Issue in Python - python

I am new to Python and I have been working with it for a bit, but I am stuck on a problem. 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. Am I using the ctr variable wrong?
EDIT:
I am trying to print out how many times the function is recursed. So ctr would be a counter for each recursion.

The variable ctr in your example will always be 1 because of the order of the recursive call stack. As one value of ctr is returned, then the call stack will start returning the previous values of ctr. Basically, at the very last recursive call, the highest value of ctr will be returned. 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. Let's say I input parameters into collatz that would result in five total calls of the method. 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.
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. 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)

I changed your recursive calls to set the value received back from the recursive calls into 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)

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. 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. 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. (Note that the collatz function doesn't return anything now, the answer is in the global variable).
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). 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:
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:
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 #???

The variable will return the final number of the collatz sequence starting from whatever number you put in. The collatz conjecture says this will always be 1

Related

Recursive behavior

Why does the following executes such that the print statement is called as often as it recursed but the count variable, count, when x == 1 is never reached.
def count_bits(n, count = 0):
x = n % 2
if n == 1:
return count + 1
if n < 1:
return count
if x == 1:
count += 1 # when x == 1
count_bits(int(n/2), count)
print("counter")
return count
why is it necessary to recurse with the return statement? Because if the recursive call is above the return statement the code
returns the wrong output but with the recursive call called with
return keyword, everything works well. Typically, the print statement
prints 'counter' as often as it recursed showing that the recursive call
works.
On the other hand, if "return" follows after the recursive call, it returns the count from the base condition, correctly.
def count_bits(n, count = 0):
x = n % 2
if n == 1:
return count + 1
if n < 1:
return count
if x == 1:
count += 1
return count_bits(int(n/2), count)
You have to return recursion result, as reccurent function meaning is to count current step you have to get result of previous step
F_k = F_k-1 * a + b # simple example
Means that you have to get result of F_k-1 from F_k and count current result using it.
I advised you to use snoop package to debug your code more efficient , by this package you can track the executing step by step.
to install it run:
Pip install snoop
Import snoop
add snoop decorator to count_bits()
for about the package see this link
https://pypi.org/project/snoop/
the difference in the output between the two methods is because of the way in which python handles fundamental data types. Fundamental data types such as float, ints, strings etc are passed by value, whereas complex data types such as dict, list, tuple etc are passed by reference. changes made to fundamental data types within a function will therefore only be changed within the local scope of the function, however changes made to a complex data type will change the original object. See below for an example:
x = 5
def test_sum(i : int):
print(i)
i += 5
print(i)
# the integer value in the global scope is not changed, changes in test_sum() are only applied within the function scope
test_sum(x)
print(x)
y = [1,2,3,4]
def test_append(l : list):
print(l)
l.append(10)
print(l)
# the list in the global scope has '10' appended after being passed to test_append by reference
test_append(y)
print(y)
This happens because it's far cheaper computationally to pass a reference to a large object in memory than to copy it into the local scope of the function. For more information on the difference, there are thousands of resources available by searching "what is the difference between by reference and by value in programming".
As for your code, it seems to me the only difference is you should alter your first snippet as follows:
def count_bits(n, count = 0):
x = n % 2
if n == 1:
return count + 1
if n < 1:
return count
if x == 1:
count += 1 # when x == 1
# assign the returned count to the count variable in this function scope
count = count_bits(int(n/2), count)
print("counter")
return count
The second code snippet you wrote is almost identical, it just doesn't assign the new value to the 'count' variable. Does this answer your question?

Understanding the code behind a gridtraveller

I found a basic code in python to find the numbers of paths you can take in a (m,n) grid if you can only go either down or right.
def gridtraveller(m,n):
if m == 0 or n == 0:
return 0
elif m == 1 or n == 1:
return 1
return gridtraveller(m-1,n) + gridtraveller(m,n-1)
But I dont understand why is this working for two thing:
What does def something (m,n) do ?
And why does here we return the definition ? ( I do understand why we return
m-1 and n-1 , but I don't understant the concepte of a def returning a def)
Thanks to you and sorry english is not my first language.
In Python the def keyword is simply used to define a function, in this case it's the function gridtraveller(m,n). What you're seeing with that last return statement is actually a function returning the value of another function. In this case it's returning the value of another call to gridtraveller, but with different parameter values; this is called recursion. An important part of recursion is having appropriate base cases, or return values that won't end in another recursive call(i.e. the return 0 or return 1 you see).
It can be easier to understand by simply stepping through a few iterations of the recursive calls. If your first function call starts with m = 2 and n = 1, the first call will end with return gridtraveller(1,1) + gridtraveller(2,0). The first call in that statement will then return 1 since either m or n are 1 and the second returns 0 since n = 0 here giving a total result of 1. If larger values of m and n are used it will obviously result in a higher number since more calls to gridtraver(m,n) will happen.

local variable 'i' referenced before assignment

Trying out some recursion based questions, came across this error.
'i' is the number of digits, which increments by 1. Where have I gone wrong?
determine no. of digits by repeated div by 10
i = 0
def dig(n):
if n < 10:
i += 1
return
else:
i += 1
dig(n//10)
print(i)
dig(123)
Your code has several issues. You define i outside your function, but only use it inside the function, that's causing the main issue. However, your function also has several logic errors.
It looks like you were going for:
def dig(n):
if n < 10:
return 1
else:
return 1 + dig(n//10)
print(dig(123))
But if you really want to use a global variable to 'count' the digits:
digits = 0
def dig(n):
global digits
digits += 1
if n > 10:
dig(n//10)
return digits
print(dig(123))
Note that this has the problem that it only works once. If you call it again, you'll have to reset digits to 0 again first. It's not the best way to go and avoiding globals is generally a good idea.
If you need your function to include the call to print() instead of printing the result, you can modify the first solution like this:
def dig(n, c=1):
if n < 10:
print(c)
else:
dig(n//10, c+1)
dig(123)
But generally it tends to be more useful to have a function that returns a result, so it can be used in other places, instead of just printing a result once.
You indicated you need to store the count in a variable, which makes little sense if you were also asked to write a recursive function. Without recursion, it would look more like:
def dig(n):
digits = 1
while n > 10:
n = n // 10
digits += 1
return digits
print(dig(123))
Here is the correct recursive function:
def dig(n, i = 0):
#base case
if n < 10:
return i + 1
#recursion
return dig(n//10, i + 1)
print(dig(123))
#output: 3

Finding multiples using recursion

Given 1 to 100 numbers, for multiples of 3 it should print "he" ,for multiples of 5 it should print "llo" ,for both multiples of 3 and 5 it should print "hello".
This is what I have:
for i in range (1,100):
if(i%3==0):
print("he")
elif(i%5==0):
print("llo")
elif(i%3==0 and i%5==0):
print("hello")
How would I do this recursively?
How about the code below?
def find_multiples(current, last_num=100):
# Base Case
if current > last_num:
return
result = ""
if current % 3 == 0:
result += "he"
if current % 5 == 0:
result += "llo"
if result:
print(f"{current}: {result}")
find_multiples(current+1, last_num)
find_multiples(1)
Base case is if current reaches last_num or the maximum number you'd like to check.
Here is a general outline for doing simple recursive things in python:
BASE_CASE = 1 #TODO
def f(current_case):
if current_case == BASE_CASE:
return #TODO: program logic here
new_case = current_case - 2 #TODO: program logic here ("decrement" the current_case somehow)
#TODO: even more program logic here
return f(new_case) + 1 #TODO: program logic here
Of course, this doesn't handle all possible recursive programs. However, it fits your case, and many others. You would call f(100), 100 would be current_value, you check to see if you've gotten to the bottom yet, and if so, return the appropriate value up the call stack. If not, you create a new case, which, in your case, is the "decrement" logic normally handled by the "loop" construct. You then do things for the current case, and then call the function again on the new case. This repeated function calling is what makes it "recursive". If you don't have an "if then" at the beginning of the function to handle the base case, and somewhere in the function recall the function on a "smaller" value, you're probably going to have a bad time with recursion.
This recursive function prints multiples of a number! hope it helps
def multi(n,x):
if x == 12:
print(n*x)
else :
print(n*x,end =",")
multi(n,x+1)
print(multi(4,1));

Calculate the sum of the digits of a number recursively in Python

I'm having troubles with a recursive function in Python. The objective is for the function to calculate the sum of the digits of a number recursively.
This is what I have so far -- I realise that this version isn't as succinct as it could be, but right now I'm just trying to understand why it isn't working as is:
total = 0 #global variable declaration
def digit_sum(n):
global total #to be able to update the same variable at every level of recursion
total += n % 10 #adding the last digit to the total
n //= 10 #removing the last digit of the number
if n < 10:
total += n
return total
else:
digit_sum(n)
print 'The return value of the function is: ', digit_sum(12345)
print 'The final value stored in total is: ', total
I obtain the following output:
The return value of the function is: None
The final value stored in total is: 15
My function is somewhat working, since the final value stored in the global variable total is correct, but printing the function output returns None instead of 15.
Could you please help me understand why?
Thank you.
Interesting problem, and an interesting solution! Let me debug with a more simple number - 421.
On first call, total is assigned the value 1 and n becomes 42. The else branch gets executed.
On second call, total gets value of 3 and n becomes 4. The if branch is executed and the value total = 7 is returned.
So, why are we seeing the None? Let's inspect the call-stack:
> digit_sum(n = 421)
> > digit_sum(n = 42) # call to digit_sum from inside digit_sum
> -< 7 # value returned by inner/second call
> None
As you can notice, the value being returned by the second call is received by the first call, but the first call doesn't return the value being returned by the second call, so that's why you are seeing None.
But why does't first call return the value being returned by the second call?
Because of this line:
else:
digit_sum(n)
You are calling the function a second time, but you are not returning its return value.
Hope it helps! :)
The problem is that you didn't add a return statement in your else clause.
Adding 'return digit_sum(n)' should solve your problem:
if n < 10:
total += n
return total
else:
return digit_sum(n)
Example
When you have a recursive function (I'll take n! as example), calls are made until you reach a 'base case' (2 in n! and for you if n<10).
Let's take a look at factorial:
def fact(n):
if(n<=2):
return n
else:
return n*fact(n-1)
Without the return statement in else clause, if you ask for fact(4), this will also return none.
Here are the 'calls' with the return statement:
return (4*fact(3))
return (4*(3*fact(2)))
return (4*(3*(2)))
Which gives 24.
Here are those without:
(4*fact(3))
(4*(3*fact(2)))
(4*(3*(2)))
So the calculus is made, but nothing is returned.
I hope this will help you to understand.
NB: Here is a factorial implementation where recursivity is explained.
my solution is
def f(n):
if n/10 == 0:
return n
return n%10 + f(n/10)
output:
f(12345) = 15

Categories

Resources