I'm working through Downey's How To Think Like a Computer Scientist and I have a question regarding his print_backward() function for Linked List.
First, here's Downey's implementation of a Linked List in Python:
class Node:
#initialize with cargo (stores the value of the node)
#and the link. These are set to None initially.
def __init__(self, cargo = None, next = None):
self.cargo = cargo
self.next = next
def __str__(self):
return str(self.cargo)
We give this class the following cargo and link values:
#cargo
node1 = Node('a')
node2 = Node('b')
node3 = Node('c')
#link them
node1.next = node2
node2.next = node3
To print the linked list, we use another of Downey's functions.
def printList(node):
while node:
print node,
node = node.next
>>>printList(node1)
>>>a b c
All very straightforward. But I don't understand how the recursive call in the following function allows one to print the linked list backwards.
def print_backward(list):
if list == None : return
print_backward(list.next)
print list,
>>>print_backward(node1)
>>>c b a
Wouldn't calling "list.next" as the value of print_backward simply give you "b c"?
Note: Several people below have pointed out that this function is badly designed since, given any list, we cannot show that it will always reach the base case. Downey also points out this problem later in the same chapter.
In the forward-printing version, it prints each node before doing the recursive call. In the backward-printing version, it prints each node after doing the recursive call.
This is not coincidental.
Both of the functions recurse until the end of the list is reached. The difference is whether printing happens during this process or afterward.
Function calls use a stack, a last-in first-out data structure that remembers where the computer was executing code when the function call was made. What is put on in the stack in one order comes off in the opposite order. Thus, the recursion is "unwound" in the reverse order of the original calls. The printing occurs during the unwinding process, i.e., after each recursive call has completed.
def print_backward(list):
if list == None : return
print_backward(list.next)
print list,
Wouldn't calling "list.next" as the value of print_backward simply give you "b c"?
No; picture what happens when a->b->c gets passed to print_backward:
"[b c]" is passed to print_backward and then "a" is printed.
But "print_backward", before "a" is printed, calls itself. So:
[ a b c ] is not None, so b->c gets passed to print_backward
[ b c ] is passed to print_backward
[ c] is passed to print_backward
None is passed to print_backward
which returns
and then "c" is printed
and then "b" is printed
and then "a" is printed
quit.
If list isn't None, it calls print_backward, then prints the first member of the list. Expanded, this is exssentially what happens. You can see that when calls start returning, 'c' is printed, then 'b', then 'a'.
It looks like when actually printing a list, it prints the first node
print_backward(list='a','b','c')
print_backward(list='b','c')
print_backward(list='c')
print_backward(list=None)
list is None, so return
print 'c'
print 'b','c'
print 'a','b','c'
Sometimes I find it easier to think of recursion as merely constructing a list of calls to be made in a certain order. As the function continues, it builds up a bunch of calls until it finally gets to the base case. The base case is the situation where no further breaking down the of program is necessary; in this function, the base case is when there is nothing to print, in which case we just leave without doing anything with return.
The cool stuff usually happens on the way back as we unwind the recursive stack of function calls. currently, print_backward has been called on each element of the list, and it will now 'unwind', finishing the most recent calls first and the earlier calls last. This means that the 'instance' of print_backward created when you call it on the last element is the first one to finish, and thus the last element is the first one to be printed, followed by the second to last, third to last, etc., until the original function finally exits.
Take a look at this representation of what happened:
print_backward(node1) #first call to print_backward
print_backward(node2) #calls itself on next node
print_backward(node3) #calls itself on next node
print_backward(None) #calls itself on None. We can now start unwinding as this is the base case:
print Node3 #now the third invocation finishes...
print Node2 #and the second...
print Node1 #and the first.
While the function is called first on the earlier elements, the part that actually prints that element comes after the recursive call, so it won't actually execute until that recursive call finishes. In this case, that means that the print list part won't execute until all of the later elements have been printed first (in reverse order), thus giving you the list elements printed backwards. :D
It's using recursion. It "zips" all the way down until it gets to the end, then it prints every element as each call returns. Since the first one to get to print is the most recent called, it prints the list backwards.
No. There's two kinds of recursion:
Tail recursion: if there is nothing to do after the function returns except return its value. Function calls are not stacked.
Recursion that finds the base case first (in this case, null, then backwardly processes the list). Each function call is pushed into the stack, for later processing. In your exemple, the function is stacked as 'a'->'b'->'c'->null, then as the stack is popped, the author showed that by printing backwards: `if null return: print 'c' -> print 'b' -> print 'a'
In your case, the author only demonstrated a different concept of recursion, and used that to print the list backwards.
Your nodes look something like this:
node1 node2 node3
'a' => 'b' => 'c' => None
At the first call to print_backward, the variable list has the value 'a', subsequent calls to print_backward move one further down the line. Note that none of them print anything until you hit the guard (None) at which time, things get printed from the back to front as the print_backward that received node 'c' must return before the print_backward that received node 'b' can print (because the print statement is after the function call) and so on.
While I recognize that this is somebody else's code, there are a few things in here which are bad practice -- Best I tell you now while you're learning rather than later. First, don't use list as a variable name since it is the name of a builtin function/type in python. second the equality test if obj == None is better done by if obj is None, finally, it's always a good idea to have your classes inherit from object (class node(object):) as that makes it a new-style class.
Related
I'm trying to write an algorithm in python to print out all paths from the root of a (binary) tree to each leaf. Here's my code:
def fb_problem(node, curr_trav):
curr_trav = curr_trav + [node]
if node.left is None and node.right is None:
for path_node in curr_trav:
print path_node.data
print "XXX"
if node.left is not None:
fb_problem(node.left, curr_trav)
if node.right is not None:
fb_problem(node.right, curr_trav)
fb_problem(root, [])
I keep a list of nodes in the current traversal, and when I've reached a leaf, I print out the list. I'm misunderstanding something about the way python passes objects though. I thought that as each recursive call completes and is popped off the stack, the original curr_trav variable would not be affected by what the recursive call did. However, it seems as if the line
curr_trav += [node]
Is mutating the original list. The += operator returns a new list, as opposed to .append(), which actually mutates the original object. So shouldn't this call just be reassigning the name given to the object in the function, not mutating the original object? When I change the line to something like
t_trav = curr_trav += [node]
Everything works fine, but I don't understand what the problem with the original line was. Please let me know if my question is unclear.
With python it is neither by value or reference. It is a combination of both, and depends on the type of object being passed into the function. For example if a mutable type such as dict, list etc is passed in it will pass the reference. Whereas with a immutable type such as a str it will be by value. A good read on this subject is by Jeff Knupp.
The issue with your original code curr_trav += [node] is that it is adding the values of [node] to curr_trav and setting the reference to the new list. Because it passes the reference for curr_trav it will be changed through each subsequent iteration.
Your understanding of += is not quite correct. All operators in Python are really just shortcuts. For example, a + b is a.__add__(b) if a has an __add__ method. If a does not, it is b.__radd__(a). If b doesn't have that method, an error is raised. Usually, a += b behaves quite like a = a + b, but in the case of mutable objects, it usually doesn't. That is because a += b is a.__iadd__(b) if a has the __iadd__ method. If a does not, it is the same as a = a.__add__(b). If a doesn't have that either, it is the same as a = b.__radd__(a). Since lists do have the __iadd__ method, the actual list object is changed instead of redefining curr_trav.
My function same_num takes values that are common to both sorted lists and appends them onto 'result'. It's using recursion and two offsets, pos1 and pos2 that are always initially set to 0, to compare values in the list. When running the function, it works fine the first time, however if I run the function a second time, the original result is appended with the answer I got from running it initially. Where am I going wrong?
result=[]
def same_num(list1,list2,pos1,pos2):
list1=sorted(list1)
list2=sorted(list2)
if pos1==len(list1) or pos2==len(list2):
return result
if list1[pos1]==list2[pos2]:
result.append(list1[pos1])
return same_num(list1,list2,pos1+1,pos2+1)
if list1[pos1]>list2[pos2]:
return same_num(list1,list2,pos1,pos2+1)
if list1[pos1]<list2[pos2]:
return same_num(list1,list2,pos1+1,pos2)
For example:
same_num([3,1,2,4],[3,1,2,4,5,6],0,0)=>[1,2,3,4]
Rerunning the previous example in the shell produces:
same_num([3,1,2,4],[3,1,2,4,5,6],0,0)=>[1, 2, 3, 4, 1, 2, 3, 4]
when it should still produce:
[1,2,3,4]
The problem is that result is a global variable. Globals are bad! You are adding stuff to result (result.append(...)) but never clearing it out after the first invocation of the same_num function.
(Although I can see why you are taking this approach, because conceptually it is often easier to approach recursive functions using global variables.)
If you make result a parameter of the same_num function that can be passed to recursive invocations of the same function... this issue is fixed.
def same_num(list1,list2,pos1,pos2,init_result=None):
# IMPORTANT: see remark below on why init_result=[]
# would not do what you expect
result = init_result if init_result is not None else []
list1=sorted(list1)
list2=sorted(list2)
if pos1==len(list1) or pos2==len(list2):
return result
if list1[pos1]==list2[pos2]:
result.append(list1[pos1])
return same_num(list1,list2,pos1+1,pos2+1,result)
if list1[pos1]>list2[pos2]:
return same_num(list1,list2,pos1,pos2+1,result)
if list1[pos1]<list2[pos2]:
return same_num(list1,list2,pos1+1,pos2,result)
# multiple invocations will return the same (expected) result
print( same_num([3,1,2,4],[3,1,2,4,5,6],0,0) )
print( same_num([3,1,2,4],[3,1,2,4,5,6],0,0) )
By the way, see "Common Python Gotchas: Mutable default arguments" for why I used init_result=None as the default, rather than init_result=[].
When running the function, it works fine the first time, however if I
run the function a second time, the original result is appended with
the answer I got from running it initially.
That is the exact issue. You are not emptying the previous result before you call it again. result still contains the values from the first time you ran the function.
For example, try running it like this instead:
output = same_num([3,1,2,4],[3,1,2,4,5,6],0,0)
print output
result = []
output = same_num([3,1,2,4],[3,1,2,4,5,6],0,0)
print output
Both outputs will be [1,2,3,4]
So I was creating a simple recursive function which essentially compares two strings, 'string' and 'target', and if they're the same, the function replaces the 'target' with 'rep', much like the function of find and replace you would see in a text editor. My issue is, is that the function returns nothing. Is that because it hasn't hit my base case or am I missing an extra return somewhere?
def helperfn(string,target,rep,x): # x is always initially set to zero
if x==len(string): #base case
target=rep
return target
if len(string)==len(target):
if string[x]==target[x]:
helperfn(string,target,rep,(x+1))
else:
return "string and target are not the same"
A few examples of what the expected output should be:
helperfn("noway","noway","yes",0)=>"yes"
helperfn("ok","ok","never",0)=>"never"
When you call the function recursively, you want to return that value.
return helperfn(string,target,rep,(x+1))
It's because you never returned anything explicitly in one branch. Change
if len(string)==len(target):
if string[x]==target[x]:
helperfn(string,target,rep,(x+1))
to
if len(string)==len(target):
if string[x]==target[x]:
return helperfn(string,target,rep,(x+1))
I am a novice who has just finished edX's introductory course MIT 6.00.1x; the following is related to a problem on that course's final exam (now concluded, so I can seek help). Let
def class DLLNode(object):
def __init__(self, name):
self.cargo = cargo
self.before = None
self.after = None
def setBefore(self, before): self.before = before
def setAfter(self, after): self.after = after
def getBefore(self): return self.before
def getAfter(self): return self.after
def getCargo(self): return self.cargo
be used to create a doubly linked list. Suppose node is an instance of class DLLNode that appears in a doubly linked list. Then node.getBefore() returns that node's immediate predecessor in the list, except that it returns None if node is at the front of the list and so has no predecessor.
I have written a recursive function
def firstInList(nodeInList):
""" Prints out the cargo carried by the first node in that doubly linked list
of which nodeInList is a part. Returns that first node. """
if nodeInList.getBefore() == None:
firstnode = nodeInList
print firstnode.getCargo()
return firstnode
# nodeInList.getBefore() is not None, so nodeInList has an immediate predecessor
# on which firstInList can be be called.
firstInList(nodeInList.getBefore())
that I wish to return the first node in a doubly linked list, given as argument a known node nodeInList in the list.
My problem: firstInList arrives at the correct first node, as evidenced by its printing the first node's cargo regardless of the specific nodeInList used. But whenever nodeInList is not the first node in the linked list, the return value of firstInList(node) turns out to be None rather than the desired first node. This conclusion is based on the following: If, for example, the list's first node node1 has cargo 1 and is followed by node2 with cargo 2, then firstInList(node2) == None evaluates as True but firstInList(node2) == node1 evaluates as False. A call firstInList(node2).getCargo() will return an error message
Attribute Error: 'NoneType' object has no attribute 'getCargo'
Another datum is that firstInList(node1) == node1 evaluates as True; that, at least, is as I would expect.
This suggests the firstnode found is not being returned back up the chain of recursive calls in the way I have imagined. Can anyone explain why?
(Please do not suggest that I use iteration instead of recursion. I know how to do that. I am trying to understand Python 2.7's behavior for the code as written.)
Well, it would appear that you're not returning the result of the recursion, so the function will in all cases but the degenerate simply return the default uninitialized value.
The last line should be:
return firstInList(nodeInList.getBefore())
Many thanks to Nathan Tuggy. At first I misunderstood what you were saying, but in fact you were correct.
My firstInList function worked perfectly once I changed the last line
firstInList(nodeInList.getBefore())
to read
return firstInList(nodeInList.getBefore()) .
Given the ridiculous number of hours I've spent worrying about this, I think this is a type of mistake I'm not likely to make in the future. Or if I do, I'll be able to discover the problem myself.
def a(b=[]):
b.append(1)
return b
print a()
print a()
All of a sudden i got a list with 2 elems, but how? Shouldn't b be getting set to empty list every time.
Thanks for the help
Default arguments are only evaluated once, when the function is defined. It retains the same object from one invocation to the next, which means that the same list keeps getting appended to. Use a default value of None and check for that instead if you want to get around this.
Nothing to do with closures, at least not in the usual sense.
The default value for b is not "a new empty list"; it is "this particular object which I just created right now while defining the function, initializing it to be an empty list". Every time the function is called without an argument, the same object is used.
The corrected version, for the reasons given in other answers, is:
def a(b=None):
b = [] if b is None else b
b.append(1)
return b
default arguments are evaluated (once) when the function is defined, not each time it is called.
try this:
def a(b=None):
if b is None
b = []
b.append(1)
return b
print a()
print a()