Spring Semester 2007


Lab Seven: Midterm Review
Let's consider the following function:
def fun(a, b):
    return b - 2 * a
Can you evaluate the following:
 fun(fun(1, 2), fun(3, fun(fun(4, fun(5, 6)), fun(7, 8)))) 

12, but that's too long. OK, how about just
fun(fun(2, 3), 4)

That's just about easy: 6. Do you understand how it works?

Yes: there are two identical methods working in there. They're identical, and each one starts fresh.

Then you can probably easily understand recursion. We'll see about that.

Consider the following definitions:
def alpha(x, y):
    return x + beta(y, x)

def beta(x, y):
    return y - x 
alpha(2, 3) evaluates to 1

What if we redefine beta as such:
def beta(y, x):
    return y - x
The value that we then get is 3.

What does this function do: It plays guess the number with the user.
def game(secret):
    print "You need to guess:", secret
    guess = int(raw_input("Enter guess: "))
    if guess == secret:
        print "You guessed it."
    else:
        if guess < secret:
            print "Please try higher:"
        else:
            print "Try lower."
        game(secret)
    

It does it in a special way though. Examples always help.
>>> game(23)
You need to guess: 23
Enter guess: 50
Try lower.
You need to guess: 23
Enter guess: 25
Try lower.
You need to guess: 23
Enter guess: 12
Please try higher:
You need to guess: 23
Enter guess: 23
You guessed it.
>>>

Change the function so it only allows 10 incorrect attempts. If you don't guess it in 10 attempts you should lose.

Ok, so that's one exercise for today. What does this function do:
def q(size):
    for i in range(size):
        for j in range(size):
            if i+j == size/2 or i+j == size*3/2 or i-j == -size/2 or i-j == size/2 or (i-j == 0 and i >= size/2):
                print "*",
            else:
                print " ",
        print

Take a look: Oh, so that explains it's (lowercase) name!
>>> q(25)
                        * *                      
                      *     *                    
                    *         *                  
                  *             *                
                *                 *              
              *                     *            
            *                         *          
          *                             *        
        *                                 *      
      *                                     *    
    *                                         *  
  *                                             *
*                       *                        
  *                       *                     *
    *                       *                 *  
      *                       *             *    
        *                       *         *      
          *                       *     *        
            *                       * *          
              *                     * *          
                *                 *     *        
                  *             *         *      
                    *         *             *    
                      *     *                 *  
                        * *                     *
>>> 

Consider the following function:
def sequence(num):
    if num == 1:
        print num
    else:
        if num % 2 == 0:
            print num,
            sequence(num / 2)
        else:
            print num,
            sequence(3 * num + 1)
I think I've seen it somewhere!

Well, here's how it works: Interesting! (And I can verify this by hand, too).
>>> sequence(3)
3 10 5 16 8 4 2 1
>>> sequence(16)
16 8 4 2 1
>>> sequence(27)
27 82 41 124 62 31 94 47 142 71 214 107 322 161 484 242 121 364 182 91 274 137 412 206 103 310 155 466 233 700 350 175 526 263 790 395 1186 593 1780 890 445 1336 668 334 167 502 251 754 377 1132 566 283 850 425 1276 638 319 958 479 1438 719 2158 1079 3238 1619 4858 2429 7288 3644 1822 911 2734 1367 4102 2051 6154 3077 9232 4616 2308 1154 577 1732 866 433 1300 650 325 976 488 244 122 61 184 92 46 23 70 35 106 53 160 80 40 20 10 5 16 8 4 2 1
>>> 

So the question is: how can you profile this function? Profile?

Yes, change it so it counts how long it takes to reach 1. How about this:
class thinker(object):
    def __init__(self):
        self.counter = 0
    def sequence(self, num):
        if num == 1:
            pass
        else:
            if num % 2 == 0:
                self.counter += 1
                self.sequence(num / 2)
            else:
                self.counter += 1
                self.sequence(3 * num + 1)
    def report(self):
        return self.counter
    def reset(self):
        self.counter = 0

def histogram(start, stop):
    a = thinker(); 
    for i in range(stop, start+1, -1):
        a.sequence(i)
        print str(i).rjust(2), a.report() * "*", "(" + str(a.report()) + ")"
        a.reset()

Here's how it runs: Nice. You wrapped it into an object (and changed it a little).
>>> histogram(5, 14)
14 ***************** (17)
13 ********* (9)
12 ********* (9)
11 ************** (14)
10 ****** (6)
 9 ******************* (19)
 8 *** (3)
 7 **************** (16)
>>> 

Yes. But that's not the only way we could have done it. Let's take a look at another one:
def steno(str):
    for i in str:
        if i in "aeiou":
            print "(" + i + ")",
        else:
            print i,
    print 

What does this do? It works like this:
>>> steno("wonderful")
w (o) n d (e) r f (u) l
>>> 

So what's the exercise? Change it, rewrite it, do whatever you can to eliminate the spaces in the output.

Define this:
>>> count("abracadabra", "a")
5
>>> 
There you go:
def count(str, letter):
    num = 0
    for i in range(len(str)):
        if str[i] == letter:
            num += 1
    return num

Looks good. Now define
>>> contains("wonderful", "b")
False
>>> contains("wonderful", "w")
True
>>> 
in terms of the previous method, count
How about this:
def contains(str, letter):
    return count(str, letter) > 0

Good enough! Now write a program that jumbles the letters in a word. How does it work?
import random

def jumble(word):
    while word:
        i = random.randrange(len(word))
        print word[i],
        word = word[:i] + word[i+1:]
        

Take a look:
>>> jumble("wonderful")
l e f n u r d o w
>>> 
As always, the exercises is to remove those spaces.

What does this program do?
def circular(word):
    for i in range(len(word)):
        print word
        word = word[1:] + word[0]
Take a look:
>>> circular("wonderful")
wonderful
onderfulw
nderfulwo
derfulwon
erfulwond
rfulwonde
fulwonder
ulwonderf
lwonderfu
>>> 

Now define this:
>>> hangman("python")
------ Enter: a
------ Enter: e
------ Enter: i
------ Enter: o
----o- Enter: u
----o- Enter: y
-y--o- Enter: t
-yt-o- Enter: m
-yt-o- Enter: h
-ytho- Enter: n
-ython Enter: p
>>> 
Here it is:
def hangman(word):
    mask = "-" * len(word)
    while (mask != word):
        print mask,
        guess = raw_input("Enter: ")
        newmask = ""
        for i in range(len(word)):
            if word[i] == guess:
                newmask += guess
            else:
                newmask += mask[i]
        mask = newmask

Updated by Adrian German for A202/A598