I'm trying to see the recursion trace of this function but why do i get "None" output?
def fnc(k):
if k == 0:
return 0
fnc(k-1)
fnc(k-1)
fnc(k-1)
fnc(k-1)
Your problem here is actually pretty easily identified by evaluating your code by hand for small parameters. Let's say you pass k = 1 to fnc:
def fnc(k):
# k == 1 != 0, so we skip the return
if k == 0:
return 0
# Now we call fnc four times with k - 1 = 0 as an argument
# All four function calls will return 0
# But we don't use the returned value in any way (no assignment, no return)
fnc(k - 1)
fnc(k - 1)
fnc(k - 1)
fnc(k - 1)
# The function ends without returning anything, therefore the return value is None
I don't know what you're trying to achieve, but if you want the base case value returned, this will do:
def fnc(k):
if k == 0:
return 0
return fnc(k-1)
Please compare these two functions, maybe step through it by hand and try to understand what exactly is going on.
Related
I am answering a question where we are given 3 strings: one, two and three. We want to return a Boolean to say whether we can create string three by interweaving strings one and two.
For example:
one = "aab", two = "aac", three = "aaab" -> this should return True
Whereas
one = "abb", two = "acc", three = "aaab" -> this should return False
My approach is two simply create three pointers pointing at the start of the three strings. And increment them one by one, when we find a match between strings one and three or two and three.
The above becomes tricky when pointer_one and pointer_two point to the same character in one and two respectively! In this case, we need to run a recursion.
Now my question is when we start adding recursion calls to the recursive stack. We may get to a point where we reach the end of string three, and can return True for the overall function.
HOWEVER, there are still (partially complete) recursive calls in our recursion stack! And so the this latest return True, will be passed back to the previous caller, but I want to just return True for the whole function and ignore the remaining items in the recursion stack- is there anyway to do this?
Or would I have to write code in such a way as to simply return nothing until the stack is empty, and then return True to the final call?
The code I have written is quite long so I have omitted it for now but I can include it if necessary.
Thanks!
My code:
def interweavingStrings(one, two, three, pointer_one=0, pointer_two=0, pointer_three=0):
possible_interwoven = False
while pointer_three < len(three):
if one[pointer_one] == two[pointer_two]:
possible_interwoven = interweavingStrings(one, two, three, pointer_one + 1, pointer_two, pointer_three + 1)
if not possible_interwoven:
possible_interwoven = interweavingStrings(one, two, three, pointer_one, pointer_two + 1,
pointer_three + 1)
if not possible_interwoven:
break
if pointer_two <= len(two) - 1 and three[pointer_three] == two[pointer_two]:
pointer_two += 1
pointer_three += 1
possible_interwoven = True
if pointer_one <= len(one) - 1 and three[pointer_three] == one[pointer_one]:
pointer_one += 1
pointer_three += 1
possible_interwoven = True
if pointer_two <= len(two) - 1 and pointer_one <= len(one) - 1 and one[pointer_one] != three[pointer_three] and two[pointer_two] != two[pointer_two]:
return False
return possible_interwoven
This should be part of your recursive function's structure. When you make the recursive call, if it returns True, return from the current call with a value of True.
For example:
def canweave(a,b,c):
if not c : return True
if len(a)+len(b)<len(c): return False
if a and a[0]==c[0] and canweave(a[1:],b,c[1:]):
return True # works with 1st of a
if b and b[0]==c[0] and canweave(a,b[1:],c[1:]):
return True # works with 1st of b
return canweave(a[1:],b[1:],c) # try with other chars
# return result from recursion
print(canweave("aab","aac","aaab")) # True
print(canweave("abb","acc","aaab")) # False
Observations on your code:
The result from possible_interwoven = interweavingStrings(...) should be definitive than just a possibility. When you obtain True from that call, you must be certain that the rest of the characters are "interwevable". So you should return True immediately when possible_interwoven is True. This will automatically trickle up the recursive call to produce the final result.
There are also issues with how you advance your pointers but I can't see an easy way to fix that with a simple tweak.
here's a revised version using your pointer approach:
def interweavingStrings(one, two, three,
pointer_one=0, pointer_two=0, pointer_three=0):
if pointer_three == len(three):
return True
if pointer_one >= len(one) and pointer_two >= len(two):
return False
if pointer_one < len(one) and one[pointer_one] == three[pointer_three]:
if interweavingStrings(one,two,three,
pointer_one+1,pointer_two,pointer_three+1):
return True
if pointer_two < len(two) and two[pointer_two] == three[pointer_three]:
if interweavingStrings(one,two,three,
pointer_one,pointer_two+1,pointer_three+1):
return True
return interweavingStrings(one,two,three,
pointer_one+1,pointer_two+1,pointer_three)
I have used pylint to check my code and I am receiving the following suggestion.
'Either all return statements in a function should return an expression, or none of them should. (inconsistent-return-statements)'. The code block is below. I am passing in a value and need to return which value range it belongs to. If I move the return value outside of the if it will return the incorrect count value. Any suggestions?
def findRangeValue(ranges, number):
count = -1
n = 2
for x in range(len(ranges)-n+1):
count += 1
batch = range[x:x + n]
if batch[0] < number <= batch[1]:
return count
You need confirm the function always have a non-empty (not None) return value in most common situation.
Your code will return None when all if statement in the loop failed, need add a final return value outside loop.
example code:
def findRangeValue(ranges, number):
count = -1
n = 2
for x in range(len(ranges)-n+1):
count += 1
batch = ranges[x:x + n]
if batch[0] < number <= batch[1]:
return count
return -1
print(findRangeValue([1,3,5,7,9], 4))
print(findRangeValue([1,3,5,7,9], 10))
result:
1
-1
Your function is written in a way, that it returns a value only if a condition is met i.e .
if batch[0] < number <= batch[1]:
Assume a situation you are consuming the function something like
range_val = findRangeValue([1,2,3], 3)
If the condition was met the return value will be assigned to range_val. But if the condition was not met, then the function returns nothing and your range_val becomes None.
But your code expects it to be a number and not None. So, now you need to check the return value to avoid errors - an extra line of code.
Assume you are calling this function from various parts of your code, every where you need to bring in this check - more and more code.
To avoid this, a standard to follow is that, a function always returns what was promised. If it was not able to do so, raise an Exception so that, the caller is notified without an additional check.
In your particular scenario, you can use a break statement and always return a count outside (beware, if break was not met, the value could be not what you expect) Or use an additional flag variable with break, and raise an error if the flag variable was not set.
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.
I am trying to finish 1 exercise but i cannot find a solution myself.
I get a number and function should use recursion to find a value:
if n == 1: return 1
elif n != 1: fun(n-1) + n
but i want to provide to my function 2 values, starting point and finishing point and my function finds all values in range(starting point, finishing point).
Define your function with two parameters. One of them gets idecremented when you make the recursive call, the other is kept the same:
def sumRange(start, end):
if start > end:
return None # or could signal an error
elif start == end:
return start
else:
return sumRange(start, end-1) + end
Don't forget that when you make the recursive call, you have to return the result.
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