05 Infinite loops, modulo, and random numbers

Today's lesson collects a few unrelated topics with the goal of giving
you some fun and maybe even useful homework problems to work on.

================== while loops ==============================

You've written for loops, but I haven't mentioned Python's other
kind of loop: the while loop. It looks like this:

while condition :
do stuff

condition is the same sort of thing you might see in an if statement.
You could say while x < 3, or while len(s) > 0.

You can build more elaborate conditions with and and or:

while x < 3 and len(s) > 0 :
while (x > 0 and x < 3) or len(s) > 0 :

Use parentheses whenever it gets complicated enough that you might get
confused about how to evaluate the condition. But most of the time you
won't need anything that hairy.

There's one more while loop you'll see surprisingly often in Python,
the infinite loop:

while True :
print "This will repeat forever!"

(If you try that, Control-C will interrupt the loop.)
But using a "while True" doesn't necessarily mean you expect your program
to run forever. You can use break to leave the loop. For instance,
reading commands from the user:

while True :
cmd = raw_input("Command? ")
if cmd == "quit" :
print "Bye"
break # if the user typed quit, it's time to leave
print "Your command was", cmd

=================== Math and Rounding ==================

Obviously you can do simple math, like 23 + 52, without any special
library. But if you do anything that needs complicated math, like
logarithms or cosines, you need the math module. It's straightforward:

import math
print "log(42) is", math.log(42)

It also has some constants, like math.pi and math.e.
Read more about it here: http://docs.python.org/library/math.html

But there are a couple of math operations I want to talk about that
don't require any library. First, conversions: sometimes you make a
calculation that might not come out even, but you need an integer. You
can truncate -- round down to the next-lowest integer -- with int():

>>> int(45.6)
45

Or you can round to the nearest integer with round():

>>> round(45.6)
46.0

Notice that that's still a floating point number -- it has a decimal
point. So if you really need an integer, like if you're going to use
it to pick an element in a list, you might need to say int(round(45.6)).
You'll know if you need that if you get an error like:
TypeError: list indices must be integers, not float

====================== Modulo ===========================

A less familiar operation that they don't teach in high school math,
comes up in computer programming all the time: the modulo operator.
It gives you the remainder when one number is divided by another.
It's represented by the % sign in Python (and nearly all other modern
programming langagues).

>>> 11 % 9 # pronounce this "eleven mod nine"
2
>>> 7 % 3 # "seven mod three"
1

11 divided by 9 is 1 with 2 left over; 7 divided by 3 is 2 with 1 left over.

Why is that so useful? Why would you care more about the remainder
than about the actual value of the division?

For one thing, you can use it to find out whether one number is divisible
by another. I have a Python program that shows a ruler, and it uses % to
make every 5th tic a little longer, and tics that are multiples of 10
really long. Something like this:

for i in range(0, 21) :
if i % 10 == 0 :
print "-----"
elif i % 5 == 0 :
print "--"
else :
print "-"

It's handy in lots of other ways too (as you'll see in the homework).
The most important thing to remember about modulo is that if you take
x % y, you'll always get a number from 0 to y-1.

==================== random ==============================

It might seem sort of random to be talking about random numbers this
early in a beginner course, but there are so many fun things you can
do with random numbers. And Python's random module is really easy to use.

You learned in the last lesson how to import modules. To use random
numbers, just say

import random

somewhere near the top of your file. Then when you use anything from
random, make sure you call it random.whatever.

The two most useful random functions are randint() and choice().
randint() gives you a random number between two boundaries, including
the boundarkes: random.randint(0, 1) will give you either a 0 or a 1,
random.randint(0, 255) will give you a number from 0 to 255 inclusive.

random.choice() is even more useful: give it a list, and it picks a
random element of the list. So if you have a list of words like

verbs = [ "programmed", "waited", "bubbled", "bounced", "danced the Macarena" ]
random.choice(verbs)

it will pick one of those verbs.

For other stuff it can do: http://docs.python.org/library/random.html

That's it for the discussion -- let's do some exercises!

====================== Homework =============================

1. Suppose you have a list of colors, like
colors = [ "red", "orange", "yellow", "green", "blue" ]
and you have a bunch of things (say, 20 of them) that you
need to color without repeating. How would you use the modulo
operator, %, to step through the colors in order, printing out one
after another and going back to red after blue?

Hint: remember, x % y always gives you a number between 0 and y.
What does y have to be so that it always gives you something that
could be an index of the list?

2. How would you write a program to choose a random file from a
directory full of files? Assume the directory is in some fixed place,
like "/usr/share/backgrounds" or "/home/yourname/Backgrounds".
Hint: os.listdir(dirname) will give you a list of all the files in
a directory. Of course you have to import os first.

Why would you want to do this? In my case, I have a big collection
of desktop background images, and every time I log in, I have a
script that gives me a different wallpaper image that day. (I use
hsetroot -center filename to set the wallpaper under openbox; Gnome
or KDE users might need a different method.) It's fun to see what
image I'll get each day. For a while I did the same thing with my
beep tone, so if anything happened to make my computer beep, I
might hear a crow or a wolf or a grey whale. But it drove my
husband crazy, so I stopped in the name of marital harmony.

3. Make a list of nouns and a list of verbs. Then write a program that
makes a random sentence by choosing a random noun and a random verb.

You may notice that my verbs are intransitive -- they can be used on
their own. So you can say "the cat waited", whereas a noun-verb sentence
wouldn't work if you used a transitive verb like "the cat fixed".

4. (Optional) Change your program to add another sentence type or
two -- maybe add a list of transitive verbs and make it sometimes
choose noun-trans verb-noun sentences, other times just
noun-intrans verb sentences. Or add a list of adjectives and add
adjectives to your sentences. How would you use random and modulo
together to say "sometimes do this type of sentence, other times
do this type"?

The object here is to have fun writing a program that makes
interesting, wacky sentences.

If you get ambitious and want to spend some more time, try do this
in a loop, taking input from the user each time, and let the user
quit by typing q or quit or whatever. You could even ask the user
for a noun or verb and then make a sentence using it.

By the way, this sort of program is especially fun as a CGI script on
the web, like http://itsthisforthat.com/

I wrote a program like that back when I was going to a lot
of astronomy star parties. People were posting observing reports
that all sounded pretty much alike, and to make fun of them I wrote:
http://shallowsky.com/obsreport.cgi
(Sadly, it's written in Perl because I hadn't learned Python yet.)
The best part is that the guy whose reports I was particularly
parodying didn't realize it was a program, and replied "Nice
observing report, Akkana, but it seems a little repetitive."
Several people told him to try hitting reload a few times. :-)