Week 4: indefinite loops, functions

Monday

import random

python has a random library that you can import into your programs and use to generate random numbers or choices. The actual numbers are pseudo-random, meaning they are not really random. For our purposes (simple games), they are random enough.

syntax

First import the random library:

from random import *

Then use one of the various functions in the library. The most commonly-used functions are:

choice(seq) -- choose one from a sequence
randrange(start,stop) -- chose a random number from [start,stop-1]
shuffle(list) -- shuffles a list
random() -- returns a random float from [0,1)

examples

To simulate flipping a coin, you could use any of these:

flip = choice("HT")
flip = choice(["heads","tails"])
flip = randrange(2)    # assume 0 is heads, 1 is tails
flip = random()        # assume < 0.5 is heads

For example:

>>> from random import *
>>> for i in range(10):
...   flip = choice(["heads","tails"])
...   print(flip)
...
tails
heads
tails
heads
heads
heads
heads
tails
tails
tails

To simulate rolling 6-sided dice:

result = randrange(1,7)

To shuffle a list:

>>> L = list("ABCDEFG")
>>> print(L)
['A', 'B', 'C', 'D', 'E', 'F', 'G']
>>> shuffle(L)
>>> print(L)
['E', 'B', 'C', 'G', 'F', 'A', 'D']
>>> shuffle(L)
>>> print(L)
['G', 'B', 'D', 'A', 'F', 'E', 'C']

So how would you use random to have the computer pick rock, paper, or scissors?

indefinite while loops

A while loop should be used anytime we want our programs to loop, but don’t know ahead of time how many iterations are needed. Examples include:

  • games, where we don’t know how many turns will occur

  • getting input from the user, where we might have to give them more than one chance (in case they enter invalid data)

  • simulations, where they might continue running until some condition occurs

For games, like tic-tac-toe, the game might take 5 turns or more (but not more than 9). In pseudo-code, this would be:

while game is not over:
  # player takes a turn
  # update/display the new board

syntax

The syntax is exactly the same as the if statement:

while some condition is True:
  # do this indented code block
  # could be 1 or more lines
  # then go back to the top and recheck the condition

However, as long as the condition is True, it will keep looping and repeating the code block.

The if statement will do the code block once if the condition is True. The while loop will recheck the condition after the code block, and repeat it if the condition is still True.

example

Here’s a program that rolls two 6-sided dice, and keeps rolling until the sum of the dice equals a user-entered number:

from random import *

def main():
  utotal = int(input("Number 2-12: "))
  dtotal = 0
  while dtotal != utotal:
    dice1 = randrange(1,7)
    dice2 = randrange(1,7)
    dtotal = dice1 + dice2
    print("%d %d (%d)" % (dice1,dice2,dtotal))

main()

And here’s a run of the program, where the user enters 6:

Number 2-12: 6
5 4 (9)
6 6 (12)
4 5 (9)
6 2 (8)
3 4 (7)
6 2 (8)
6 4 (10)
2 1 (3)
6 5 (11)
5 6 (11)
3 3 (6)

Wednesday

valid user input

Another place to use a while loop is when getting input from the user, and making sure they entered a valid choice. Here’s a "rock, paper, scissors" game example:

rock, paper, or scissors? zebra
please enter rock, paper, or scissors!!
rock, paper, or scissors? 99
please enter rock, paper, or scissors!!
rock, paper, or scissors? rock

Here’s one way to write that loop, using a list of valid inputs and the in operator:

    valids = ["rock","paper","scissors"]
    userinput = input("rock, paper, scissors? ")
    while userinput not in valids:
        print("please enter rock, paper, or scissors!!")
        userinput = input("rock, paper, or scissors? ")

Notice how the userinput variable is set before the while loop starts, and the while loop code block is only run if the initial input is not valid.

first look at functions

As our programs get larger and more complex, using functions becomes a neccessity. Designing and writing your programs using functions makes them easier to write, read, test, and debug.

The above code is probably just one part of a larger program (the rock paper scissors full program). This chunk of code would make a good function in a larger program. Here’s how to write the same code as a function:

from random import *

def main():
    valids = ["rock","paper","scissors"]
    userinput = getUserInput(valids)
    print("You chose %s" % (userinput))
    computer = choice(valids)
    print("I chose %s" % (computer))

def getUserInput(valids):
    userinput = input("rock, paper, scissors? ")
    while userinput not in valids:
        print("please enter rock, paper, or scissors!!")
        userinput = input("rock, paper, or scissors? ")
    return userinput

main()

If we run the above program, here are the major steps as they happen:

  • the random library is imported

  • a function called main() is defined

  • a function called getUserInput() is defined

  • the main() function is started/called

  • in main(), a python list (valids) is defined

  • next in main(), the getUserInput() function is called:

    • the valids list is sent to the function

    • in the function, the user is asked for input

    • once we have a valid input, that string is returned to main()

  • back in main(), the user’s input is assigned to the userinput variable

  • and the program keeps going with a print(), the computer’s choice, and another print()

function syntax

Just like we’ve been doing all along with main(), a function is just an indented block of code with a name. Here’s a simple function:

    def happybirthday(name):
       """display happy birthday song for name"""

       print("Happy Birthday to you.")
       print("Happy Birthday to you.")
       print("Happy Birthday, dear %s." % (name))
       print("Happy Birthday to you!")

       return

Some things to note about the above function:

  • it has one parameter, the name variable

  • it has four print statements

  • the return at the end signals the end of the function, but is not always necessary

Here’s an example of how the above function might be called from main():

    def main():
      name = input("Who's birthday is it? ")
      happybirthday(name)

Whatever the user types in, it is stored in the variable name. That data is then sent to the function, for use in the print statements. In main(), the variable name is used as an argument in the call to the happybirthday() function. When writing and calling functions, the number of arguments must match the number of parameters.

I also don’t have to use a variable as an argument. I could just use a string argument, like this:

happybirthday("Ravi")

which would print:

Happy Birthday to you.
Happy Birthday to you.
Happy Birthday, dear Ravi.
Happy Birthday to you!

Friday

stack diagrams

Understanding stack diagrams helps you see how the computer works, and why python does what it does. It will also be very valuable in week 11 when we learn recursion (functions calling multiple copies of themselves).

A "stack" is just a data structure used by many programs. A stack is useful because it keeps track of where we are, and allows backtracking (e.g., hitting "undo" in your editor, or hitting the "back" button on your browser).

If you think of a stack of books or a stack of trays, you generally add items to the stack and remove items from the stack at the top end. This is different from a queue data structure (like a line at the bank or the grocery store) where items (people) are added at the back of the queue and removed (they checkout or are served) from the front of the queue.

So for your browser, say you visit espn.com, then swarthmore.edu, then cnn.com, and finally google.com. At this point, the broswer stack would look like this, with the current site on the top of the stack, and your history recorded in the rest of the stack:

google.com
cnn.com
swarthmore.edu
espn.com

If you now hit the "back" button, the browser would "pop" the stack and remove the top item (google.com) and go to whatever is next on the stack (cnn.com).

As functions are executed in our program, they are placed on the top of the stack. When the function executing is done, it is removed from the top of the stack and the function below resumes execution. This is how the computer keeps track of the program flow.

For example, if you have a main() function called first, and then main() calls another function, say func1(), and before func1() returns it calls another function, say func2(), then at this point "the stack" would look like this:

func2
func1
main

In addition to just showing what functions are on the stack, we will also include their variables and what data they refer to.

#
# drawing the stack:
#
# - when a function is called, make a stack frame (rectangle) for it on the stack
# - if one function calls another, the second stack frame goes on TOP of the stack
# - for each stack frame, when a variable is declared:
#     > put the variable name in the stack frame
#     > draw a small box next to the variable
#     > put the data value to the right, outside the stack frame
#     > draw an arrow from the small box to the data value
#   For Example, if x=5 is in main(), it will look like this:
#
#           STACK              DATA
#        -------------
#        |main       |
#        |           |
#        |   x []----|----------> 5
#        |           |
#        -------------
#
# - if a variable is updated or reassigned, update the data or redraw
#   the arrow to the new data
# - when a function finishes, we erase the stack frame off the top of
#   the stack and the program continues in the stack frame that is
#   now on TOP of the stack
#

# try drawing the stack for this program.
# stop your drawing just before the lettercount function returns

def main():
    text   = "mississippi"
    letter = "i"
    result = lettercount(letter,text)
    print("Number of %s's in %s: %d" % (letter,text,result))

def lettercount(char,text):
    """count and return how many of char are in text"""
    count = 0
    for i in range(len(text)):
        if text[i] == char:
            count = count + 1
    # after for loop is done, return the count
    return count

main()

For the above program, here’s what the stack would look like, just before the return count line in lettercount(..):

stack

In the above diagram, count was initially zero, then we added 1 to it each time the for loop counted another "i" in "mississippi".

function terms

Here’s a summary of the function terminology we have been using, for the above lettercount(char,text) function:

argsparams

function scope

The term scope just refers to where a variable is accessible. Variables (and the data they reference) in one function are local (ie, not global) to that function. If you need the data from one function in another function, you should pass the data from function to function using arguments and parameters, or return the data from the called function back to the calling function

For example, in the above letter count program, in the main() function, the variables that are in scope are: text, letter, and result. If you try to print(count) in main() the program will crash, because main() doesn’t have a variable called count.

The count variable is only accessible/active in the lettercount(..) function. We use the return count statement to send the data stored in count back to main(), where it is assigned to the variable result.

Similarly, if you try to use or print the letter variable in the lettercount(..) function, it will crash. The letter variable is only in scope in main().

more functions

See the FRIDAY file for more functions to write!