I Don't Understand This Use of Recursion - python

I was reading through the answers earning a "reversal" badge and I found a question regarding recursion where the OP didn't bother to do much of their homework assignment up front. Aside from some really funny answers, #machielo posted an answer in python that I had to run on my machine to get a grip on. I'm still not understanding it.
def recursive(x):
if x > 10:
print recursive(x/10)
return x%10
>>> recursive(2678)
2
6
7
8
I tried out my first guess, but I know it's wrong
>>> 2678/10
267
>>> 267/10
26
>>> 26/10
2
>>> 2%10
2
Okay...that's the two. How does this evaluate the output of the other numbers in x?
EDIT
It's the print statement that I don't get here. I modified the code as such:
>>> def recursive(x):
if x > 10:
print x
print recursive(x/10)
return x%10
>>> #I will comment the interpreter session here...
>>> recursive(2345)
2345 # first feed in...print the raw number `x`
234 # 2345/10 does equal 234...the 5 is being held back somewhere...
23 # and each pass through the recursive loop removes the last digit...
2 # but where was it being stored at, so that each evaluation of
3 # x > 10 finally started returning False
4 # and returns the number, exiting the function
5 # ...
I'm thinking that during each run through, the call to print recursive(x/10) creates a new function object, each with it's own brand new base case and input...
Another hint, anyone?
FINALLY
Thanks to everyone. I feel I understand this now...the trick wasn't so much print as it was x%10. 2345%10 == 5...
>>> def recursive(x):
print "Raw `x`:", x
if x > 10:
print "Recurse `x`:", x
print recursive(x/10)
print "Last `x`:", x
return x%10
>>> recursive(2345)
Raw `x`: 2345
Recurse `x`: 2345
Raw `x`: 234
Recurse `x`: 234
Raw `x`: 23
Recurse `x`: 23
Raw `x`: 2
Last `x`: 2
2
Last `x`: 23
3
Last `x`: 234
4
Last `x`: 2345
5
Also, credit to whoever went in and updated the initial answer that I previously linked to...I'm about to upvote your comment:
>>> def recursive(x):
if x >= 10:
print recursive(x/10)
return x%10

I think that adding a few print statements it's really helpful:
def recursive(x):
print '[start] recursive({0})'.format(x)
if x > 10:
print recursive(x/10)
print '[return] recursive({0}) = {1}'.format(x, x%10)
return x%10
print recursive(2678)
The output is:
[start] recursive(2678)
[start] recursive(267)
[start] recursive(26)
[start] recursive(2)
[return] recursive(2) = 2
2
[return] recursive(26) = 6
6
[return] recursive(267) = 7
7
[return] recursive(2678) = 8
8

Stepping through your example in pseudocode (number of dashes indicates recursion depth):
-call recursive(2678)
--2678 > 10, call recursive(267)
---267 > 10, call recursive(26)
----26 > 10, call recursive(2)
-----return 2%10 (which is 2)
----print 2, then return 26 % 10 (which is 6)
---print 6, then return 267 % 10 (which is 7)
--print 7, then return 2678 % 10 (which is 8)
-return 8

This function prints out the digits of the number.
It works like this:
def recursive(x):
if x > 10:
# Divide x by 10 and round down. This chops off the last decimal place.
# Now feed that new x without the last decimal place back into recursive()
# return x's last digit
Basically, it won't print anything until x is a single digit number.
The part you're confused about is probably why it's printing out each decimal place in that order. This happens because while the function recurses, the parent function is still running.
Just try and expand the code for that single number.
Edit: I'm confusing myself as well.
Your code calls print before return, which means that when the last level of recursion finishes, the second-to-last prints out the first digit. The same goes for the next layers.

Keep in mind the call stack when considering recursion. The recursive call is pushing all recursive() function calls onto the stack before anything is printed so what you end up with on the stack is
recursive(2) # end condition is met so returns 2%10
recursive(26)
recursive(267)
recursive(2678) # the initial call
once the end condition is reached 2%10 (2) is returned to the previous function's print statement and printed, then that function returns 26%10 (6), and this continues until all the recursive function calls on the stack have returned. The result is this series of print calls:
print 2
print 6
print 7
8
8 is not actually printed; it is just returned which is fine from the interpreter. If you wanted to be sure that it printed in for example a python script you would call print recursive(2678)

Related

Augmented Assignment

I am new to the world of Python and programming in general, and today I have faced a problem with augmented assignment. I unfortunately do not understand the code, and what for i in range(multiplier) and answer *= number does. I tried understanding it but I still don't really get the logic behind it. Can somebody please explain?
number = 5
multiplier = 8
answer = 0
for i in range(multiplier):
answer *= number
print(answer)
range([start], stop[, step])
range is a function that takes a number and return a list of number from 0 ... right through to the number you gave it as an argument.
BUT THE KEY TO NOTICE IS THAT IT WILL NEVER INCLUDE THE NUMBER YOU
TOLD IT TO COUNT TO
. Example :
This is an example of giving the function range 1 argument:
>>> # One parameter
>>> for i in range(5):
... print(i)
...
0
1
2
3
4
Here is an example of giving it two arguments where the first argument is telling the function what to start the list it returns at. The second argument is where it should end:
>>> # Two parameters
>>> for i in range(3, 6):
... print(i)
...
3
4
5
Here is an even cooler example where we use the third argument as well. This argument tells the function to count from whatever number you told it to start at, right through to whatever number you told it to stop at (just like the above to examples)... only now, the third argument tells it in what steps to count:
Like count from 2 to 12 but count in 2's:
>>> # Three parameters
>>> for i in range(2, 12, 2):
... print(i)
...
2
4
6
8
10
SO....
the for loop is just iterating through that list of numbers that is given back by the function range
so lets break that for loop in to pseudo code.
***loop***
for i in range(multiplier):
answer *= number
***Pseudo code***
Give me a list of numbers
(not giving it a start value but only a value to end the count at).
The numbers has to be from 0 to multiplier (8).
so range(multiplier) -> range(8) -> [0, 1, 2, 3, 4, 5, 6, 7]
now you have a list
now you ask the compiler to go through that list.
you say : go through that list and everytime you at a new number, give it to me in the for of a variable called i.
then when you have i, you don't use it because i was just trying to loop 8 (multiplier) times... but now take the answer and add to it this (answer * number) ... this will happen 8 times because you loop 8 times

Using range, len, and % to print string

I'm a newbie to Python and one of our practice examples is in the code below but there is no feedback on what the code is doing. The question in the exercise asks "How many times is P printed out" I've provided the output below.
From what I understand on my own, the length of s is 6 and the range is 0 to 5 but since we are saying "range(len(s))" are we basically asking the loop to run 6 times?
Also, can someone help me understand the print(s[idx % 2])? How does the print statement generate the output shown below? If I were to change it to print(s[ 0:2 ] then I would be given "pypypy" horizontally which is different.
Thank you for the help.
s = "python"
for idx in range(len(s)):
print(s[idx % 2])
Output
p
y
p
y
p
y
Strings in Python are arrays of bytes representing unicode characters. However, Python does not have a character data type, a single character is simply a string with a length of 1. Square brackets can be used to access elements of the string. Therefore you can get the character at position 1 (remember that the first character has the position 0) via the following code snippet, e.g:
a = "Hello, World!"
print(a[1]) // e
print(a[2]) // l
Now looking at why your code outputs what it does (second part of your question). Recall that % is the modulo operator.
In the first run, idx = 0. So your statement becomes:
print(s[0 % 2]) = print(s[0]) = 'p' -> first letter of s.
Next idx = 1. So your statement becomes:
print(s[1 % 2]) = print(s[1]) = 'y' -> second letter of s.
Next idx = 2. So your statement becomes:
print(s[2 % 2]) = print(s[0]) = 'p' -> first letter of s.
And so on, 6 times (idx takes values of 0, 1, 2, 3, 4, 5).
P.S. Commenting on your comment regarding setting print(s[ 0:2 ]) - this is known as string slicing (essentially call out a range of characters from the string) - [0:2] means you are starting at index 0 and extend up to but not including index 2, so 'py'.
are we basically asking the loop to run 6 times?
Yes
can someone help me understand the print(s[idx % 2])?
Yes
How does the print statement generate the output shown below?
The % operator is the modulo operator. It gives the remainder of a division. For the range in question, the results are
0 divided by 2 is 0 with a remainder of 0
1 divided by 2 is 0 with a remainder of 1
2 divided by 2 is 1 with a remainder of 0
3 divided by 2 is 1 with a remainder of 1
4 divided by 2 is 2 with a remainder of 0
5 divided by 2 is 2 with a remainder of 1
As you can see, the remainder toggles between 0 and 1.
Indexing the string will thus result in s[0] which is p and s[1] which is y.

Checking for a number if first 4 digits forward equals last 4 digits backwards

If the last 4 elements stepping forward are equal to the last 4 elements stepping backwards, print the number. I have input a number where the last 4 elements are clearly palindromic. Why does this not print the number?
def checkNum(i):
num = str(i)
if num[len(num)-5:len(num)-1:1] == num[len(num)-5:len(num)-1:-1]:
print(num)
checkNum(777777)
You got the slices wrong. The left part of the string should be sliced like that: num[:4] and the right part should be sliced like that: num[:-5:-1]
Edit for the comment:
You can always print the slices you're attempting to use in the function. To get the problem, use something more visualising than 777777. For example: 123456789. Then, if you print your slices in the function, you will see the strings that you are comparing:
def checkNum(i):
num = str(i)
print(num[-4:])
print(num[-4::-1])
print(num[-5:-1])
checkNum(123456789)
The output you'll get is:
6789
654321
9876
This shows the way slices work. When you're using negative indexes, you're starting from the end with a positive step, so num[-4:] returns the last 4 characters in the original order. The negative step returns the first characters reversed. Consider some manual testing, it really saves a lot of time.
You are going backwards from len(num)-1 to len(num)-5 in the latter part of the equality!
Here's the correct version:
def checkNum(i):
num = str(i)
if num[len(num)-5:len(num)-1:1] == num[len(num)-1:len(num)-5:-1]:
print(num)
checkNum(777777)
>>> num = str(777777)
>>> print num[len(num)-5:len(num)-1:1]
7777
>>> print num[len(num)-5:len(num)-1:-1]
***None***
To access the last four from the last position, you need
>>> print num[len(num)-1:len(num)-5:-1]
7777
def checkNum(i):
num = str(i)
if num[len(num)-5:len(num)-1:1] == num[len(num)-1:len(num)-5:-1]:
print(num)
>>> checkNum(777777)
777777
You could take the first 4 characters forward ([:4]), reverse the string ([::-1]) and take the first 4 characters again (which are now the last 4 characters reversed):
def checkNum(i):
num = str(i)
if num[:4] == num[::-1][:4]:
print(num)
>>> checkNum("11118354367451111")
11118354367451111
>>> checkNum("1111835436745111")
>>>
>>> checkNum("otto")
otto
>>>

Python, For loop counter do not update?

I am writing a simple program which gives the list of all prime no. less than a given no. N.
I am facing a problem that my for loop counter check in the function primes_gen else clause do not updates. Though, when i use the same logic without a function, as mention in the second code snippet, everything works correct.
Logic applied:-
when n passed to function is <= 2, blank list is returned
when n is > 2, range(3,n,2) identifies all the odd number. This is step 1 marked in first code snippet.
second for loop, marked with step 2, checks if no. is divisible to the all no.'s less than i. If yes then check counter is incremented by +1 and this loop is ended with 'continue'
if check remains zero till the end of the for loop then i is appended to the list and the finally after completion of the loop list is returned.
Problem:-
-The list i got as a result was [2] which is wrong ans. so I wrote step 3, as marked in 1st snippet, and i found that my check counter do not updates after the 'continue', as shown in the output. when same logic is applied without a function as written in second code snippet everything works correct.
I am not able to get what is going wrong here, why does check do not updates after continue?
contents =['10']
def prime_gen(n):
num_list=[2]
if n <=2:
return []
else:
for i in range(3,n,2): #step 1
check=0
for u in (2,i): #step 2
if i%u == 0:
check += 1
continue
print (check,i) #step 3
if check == 0:
num_list.append(i)
return num_list
if __name__== '__main__':
for j in range(len(contents)):
print (int(contents[j]))
num_list = prime_gen(int(contents[j]))
print (str(num_list).replace('[','').replace(']',''))
Output
10
1 3
1 5
1 7
1 9
2
Expected ans is (when step 3 above is commented)
10
2, 3, 5, 7
Second code without function
contents = ['10'] #to-check
for i in range(len(contents)):
target = int(contents[i])
num_list= [2]
for j in range(3,target,2):
check = 0
for u in range(2,j):
if j%u == 0:
check +=1
continue
if check == 0:
num_list.append(j)
#print (num_list)
print (str(num_list).replace('[','').replace(']',''))
output
2, 3, 5, 7
Your problem took a second to find, but is very simple to solve. The issue is at your "Step 2". You have the line
for u in (2,i): #step 2
That iterates u through all the variables in the tuple you provide, namely 2 and i. Of course, i%i will be 0, so check will always be true. What you want instead is this:
for u in range(2,i): #step 2
This will iterate u through all the variables between 2 and i, as you intend.
See these two examples:
>>> i = 7
>>> for u in (2,i): print u,
2 7
>>> for u in range(2,i): print u,
2 3 4 5 6
Basically, you just forgot to use range in your for loop, and that's going to give you wildly different results than what you actually want.

How to change for-loop iterator variable in the loop in Python?

I want to know if is it possible to change the value of the iterator in its for-loop?
For example I want to write a program to calculate prime factor of a number in the below way :
def primeFactors(number):
for i in range(2,number+1):
if (number%i==0)
print(i,end=',')
number=number/i
i=i-1 #to check that factor again!
My question : Is it possible to change the last two line in a way that when I change i and number in the if block, their value change in the for loop!
Update: Defining the iterator as a global variable, could help me? Why?
Short answer (like Daniel Roseman's): No
Long answer: No, but this does what you want:
def redo_range(start, end):
while start < end:
start += 1
redo = (yield start)
if redo:
start -= 2
redone_5 = False
r = redo_range(2, 10)
for i in r:
print(i)
if i == 5 and not redone_5:
r.send(True)
redone_5 = True
Output:
3
4
5
5
6
7
8
9
10
As you can see, 5 gets repeated. It used a generator function which allows the last value of the index variable to be repeated. There are simpler methods (while loops, list of values to check, etc.) but this one matches you code the closest.
No.
Python's for loop is like other languages' foreach loops. Your i variable is not a counter, it is the value of each element in a list, in this case the list of numbers between 2 and number+1. Even if you changed the value, that would not change what was the next element in that list.
The standard way of dealing with this is to completely exhaust the divisions by i in the body of the for loop itself:
def primeFactors(number):
for i in range(2,number+1):
while number % i == 0:
print(i, end=',')
number /= i
It's slightly more efficient to do the division and remainder in one step:
def primeFactors(number):
for i in range(2, number+1):
while True:
q, r = divmod(number, i)
if r != 0:
break
print(i, end=',')
number = q
The only way to change the next value yielded is to somehow tell the iterable what the next value to yield should be. With a lot of standard iterables, this isn't possible. however, you can do it with a specially coded generator:
def crazy_iter(iterable):
iterable = iter(iterable)
for item in iterable:
sent = yield item
if sent is not None:
yield None # Return value of `iterable.send(...)`
yield sent
num = 10
iterable = crazy_iter(range(2, 11))
for i in iterable:
if not num%i:
print i
num /= i
if i > 2:
iterable.send(i-1)
I would definitely not argue that this is easier to read than the equivalent while loop, but it does demonstrate sending stuff to a generator which may gain your team points at your next local programming trivia night.
It is not possible the way you are doing it. The for loop variable can be changed inside each loop iteration, like this:
for a in range (1, 6):
print a
a = a + 1
print a
print
The resulting output is:
1
2
2
3
3
4
4
5
5
6
It does get modified inside each for loop iteration.
The reason for the behavior displayed by Python's for loop is that, at the beginning of each iteration, the for loop variable is assinged the next unused value from the specified iterator. Therefore, whatever changes you make to the for loop variable get effectively destroyed at the beginning of each iteration.
To achieve what I think you may be needing, you should probably use a while loop, providing your own counter variable, your own increment code and any special case modifications for it you may need inside your loop. Example:
a = 1
while a <= 5:
print a
if a == 3:
a = a + 1
a = a + 1
print a
print
The resulting output is:
1
2
2
3
3
5
5
6
Yes, we can only if we dont change the reference of the object that we are using. If we can edit the number by accessing the reference of number variable, then what you asked is possible.
A simple example:
a=[1,2,3]
a=a+[4]==>here, a new object is created which plots to different address.
a+=[4]==>here , the same object is getting updated which give us the desired result.
number=10
list1=list(range(2,number+1))
# list1
for i in list1:
print(list1,i)
if (number%i==0):
print(i,end=',')
number=number//i #we can simply replace it with number//=i to edit the number without changing the reference or without creating a new object.
try:
[list1.pop() for i in range(10,0,-1) if(i>number)]
#here pop() method is working on the same object which list created by number refers. so, we can able to change the iterable in the forloop.
except:
continue
i=i-1 #to check that factor again!

Categories