This document contains the instructions for the week 4 CSC148H/A48H lab. To earn your lab mark, you must actively participate in the lab. We mark you in order to ensure a serious attempt at learning, not to make careful critical judgments on your work.
This lab has you write several recursive algorithms. Remember your base cases!
Pick a navigator and driver. Expect to spend lots of time discussing the solutions; not all of them are obvious.
A palindrome is a word or phrase that reads the same forwards and backwards. Here are some examples:
Write a recursive function is_palindrome
that takes a
string as a parameter and returns True
if its parameter
is a palindrome, and False
otherwise.
count_reachable
that takes two
parameters: (i) a 2D list (i.e., a list of lists), and (ii) a 2-element
tuple. The list represents a region of cells, some of which are empty,
and some of which are blocked (i.e., full). Each element of the 2D list
is either
'E' (for empty), or 'B' (for blocked). The 2-element tuple indexes an
element of the 2D list, and represents a starting position in the
region of cells. More precisely, the first element of the tuple indexes
a 1D list, and the second element indexes an element in the list
indexed by the first tuple. count_reachable
should return the number of cells that you can reach from the starting
position, including the starting position itself. count_reachable
name from your module's
namespace to testcount's namespace (using from ... import ...
)
.
Consider the polynomial a_0x^n+a_1x^{n-1}+...+a_{n-1}x+a_n. If we try to evaluate this naively, we will first calculate x^n, then x^{n-1}, then x^{n-2} and so on. The problem is that we're doing a lot of extra work; consider that when we calculate x^n on the first iteration, we've also calculated x^{n-1} in the process, but on the next iteration we'll calculate x^{n-1} all over again. You can check badpoly.py for this inefficient evaluation method.
An alternative way to write the polynomial is (... (((a_0)x + a_1)x + a2) ... + a_{n-1})x + a_n. Now, on each iteration, all we have to do is multiply the previous result by x and add the next coefficient.
Write an iterative algorithm that implements this polynomial evaluation. Your function should take two parameters: an array containing the polynomial's coefficients, and a value for x. The coefficients represent the a_i values of each decreasing power of x in the polynomial. Here is a sample call.
>>> print polyIter ([2,3, 1], 4)
45
We can also write this function recursively. This will be your next exercise. Your recursive function should take three parameters: the coefficients array and x value as before, but also an n parameter that tells us where we are in the coefficient array. In a call of the function, n should equal one less than the length of the coefficient array. The above call on your iterative procedure would look like this for this recursive version:
print polyRec ([2,3, 1], 4, 2)
This is a clean recursive function, but now we've introduced a usability concern. Namely, the third argument passed to the function is just an artifact of our solution technique, not something the user should have to be concerned with. In addition, we can compute it from the first parameter using the length of the coefficient array. We would like to eliminate this parameter so that only the coefficients and x values are passed.
One way to do this is to create an inner function that takes one parameter n. The outer function (call it polyRec2
) will take two parameters (coefficients and x), since this is the function the user will interact with. The body of polyRec2
consists of the definition of an inner function inner
, which can access the parameters of polyRec2
by virtue of being nested within it. After defining inner
, recPoly2
can start the execution of inner
by supplying all three parameters (two of them received directly from the user; the third derived as one less than the length of the coefficient array). Implement this version of the algorithm.
This technique is called a two-level function: the outer function is what the user interacts with, and we should not require the user to supply (what he or she feels are meaningless) parameters. We instead create an inner function that uses the additional parameters, and call it from the outer function so that the actual computation can take place. Think about other advantages of this two-level approach (hint: are we doing less work when making a recursive call?). Discuss your ideas with your TA.
Now imagine a new function that we want to provide our users. The user is to supply two parameters A and B to the function, but we need to use a third parameter C to facilitate the recursion. We thus create an inner function again. However, unlike in the polynomial example, imagine that, in addition to C changing, one of the user's two parameters also changes on recursive calls. What would the definition of the inner function look like now? Discuss your answer with your TA.