Learning Recursion:
When I initially started learning about recursion at the beginning of the term, to write a recursive function I would attempt to retrace what would happen the same way a computer would to solve a recursive problem. When a list has only one layer of nested lists inside, this was possible. However, it soon occurred that neither was this a very pragmatic approach for more complicated scenarios (when there were multiple layers of embedded lists) or the right mindset of a designer. While computers are great at solving complicated paths, we’re best to leave break down problems into simple scenarios and piece them together.
In my first post on OOP, I wrote about the ease of top down approach. Designing a program was to first layout the objects and figure out the implementation later – details and the exact implementation didn’t matter because there would be a way to make it work – similar for recursive cases. Here’s a simple example of one of the questions on an earlier test – defining the function rec_len which returns the number of non-list elements in a list:
def rec_len(L):
return sum([rec_len(x) if isinstance(x, list) else 1 for x in L])
If we were to write the function above, we would divide it up by the two possibilities (when it is an empty list or when its has a value), write the two scenarios. If individually the parts work and are confirmed through tracing, then it is assumed that larger and more complicated scenarios work as well, thus there is no need to mentally trace the entire thing. If in the implementing the code, it follows a logic that makes sense, then the code will likely work in more complicated “difficult to trace” scenarios as well.
L = [] –> 0
L = [2] -> 1
L = [2,3] -> 2
L = [2,[]] -> 1
L = [2,[2,3]] -> 3
L = [2,[2,3],4] -> 4
What are recursive functions?
A recursive function is basically a function or part of a function that calls itself at least once. Let’s break down the function list_count as an example:
def max_list(L):
return max([max_list(n) if isinstance(n, list) else n for n in L])
If o = [1,[3,4],[5,6,]], the function would be start with returning [1] and inside the list, it would then go onto the second object in the list and do max_list ([3,4]) which would return 4 and append it to [1] giving [1,4]. Then it would move onto the last item in the list and do max_list([5,6]) returning 6 and appending that to the list: [1,4,6]. Finally the max of the final list would be taking producing 6. Here in this example, the number of recursion depths is 2. If the example were taking a list with a list nested three times, it would take a depth of 3 to return its corresponding result when run in the max_list function.
Why and when should you use recursion / iteration?
Simply, you’d probably know when to use it through practice and preference. A suggestion Danny had was to compare an example of a code using recursion and the same using iteration. For instance, how does the code look like for performing tree traversals in python?
From: Method: Recursion vs iteration for tree traversal in python by Damian Kao, the author has written about the comparison of a function for printing out all the children in a tree list through recursion, and has written the same function using iteration. In order to rewrite the recursive function (6 lines) using iteration( 13 lines), he had to use multiple conditionals. In the recursive function, the line:
For iteration, you need to create an extra variable to append something to (in this case, newNodes = []). Every time Python goes through iterating (for node in nodes) it must change nodes = newNodes so that it changes the subsequent “for loop” to continue the function for the rest of the children. By overwriting the original nodes to newNodes each time, it will iterate through all the children without recalling the function again.This is more complicated than using recursion because when writing iteration, you need to add an extra step where you need to specify “for node in nodes”. Recursion simply recalls the function and does this automatically for you.If you are using recursion for a small set, and it is easier and faster to write with recursion. Readability may be a better choice.
However, since for recursion, there is always a set recursion depth limit, author of this blogpost (http://blog.nextgenetics.net/?e=64), Damian, suggests that unless your recursion would need a depth greater than 1000 (or whatever the limit for python is), iteration over recursion is never a “necessity”.
Memory and recursion:
Digging a little deeper on the web, are several opinions that recursion in python is considered “incredibly inefficient” (compared to an iteration counterpart of the same function). Recursive functions copy itself into the stack and requires a new stack frame for every iteration it runs. While an iteration would return to the beginning of the loop, recursion, goes back to the beginning of the function. “creating and destroying stack frames is more expensive than a simple jump”
Trees and Comments on Other People’s Blogs:
http://yamsoupslog.blogspot.ca/ on Trees
A good point the author nicely sums up is on Search Lists, which we had exposure to in lab 4. Recursion through trees can be made more efficient in the case of structured lists. In a non structured list example, in the worst case scenario, you will have to traverse through the entire tree to determine whether a certain value exists in the tree list. On the other hand, structured lists have the potential to “shorten” the task by checking one node, and traversing only the path of the child that’s specific condition is satisfied. (ex. checking if the value is larger or smaller than n (a specified value you are looking for))
References:
http://blog.nextgenetics.net/?e=64
http://www-inst.eecs.berkeley.edu/~selfpace/cs9honline/Q2/scope.html