Week 5: more functions
Monday
allcaps(text)
example
Here’s one way to write the allcaps(text)
function we were looking at last
time:
def allcaps(text):
"""return True if given text is all capital letters"""
caps = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
for i in range(len(text)):
if text[i] not in caps:
return False # ends function if we get here
# no point in checking further
# after for loop, if we get here, must be all caps
return True
As soon as we find a letter that isn’t a capital letter, the function
ends (the for
loop, too) with the return False
line.
If the function gets through the entire for
loop, and never
does the return False
line, then the given text must be all capital
letters, so we return True
.
isVowel(letter)
example
Write a function that, given a single letter, returns True
if the
letter is a vowel, and False if not.
def isVowel(letter): """return True if given letter is a vowel""" vowels = list("aeiouAEIOU") if letter in vowels: return True else: return False
sumList(L)
example
Write a function that, given a python list of numbers, sums the numbers in the list and returns the sum of the list.
def sumList(L): """sum the items in L and return the total""" total = 0 for i in range(len(L)): total = total + L[i] return total
Wednesday
mutable objects in functions
Let’s do another stack-drawing worksheet!
#
# draw the stack and show the output for this program
#
def main():
data = [20, 88, -13, 200, 301, 199]
low = 0
high = 255
print("data before function call: %s" % (data))
howmany = limit(data, low, high)
print("data after function call: %s" % (data))
print("how many changed: %d" % (howmany))
def limit(L, min, max):
"""check if all items in list are between min and max"""
count = 0
for i in range(len(L)):
if L[i] < min:
L[i] = min
count = count + 1
elif L[i] > max:
L[i] = max
count = count + 1
# draw stack to this point, just before the return
return count
main()
The limit()
function is just going through the list of integers,
checking each one if they are less than the minimum (0) or greater than
the maximum (255). And if they are, we assign the min or the max to that
location in the list (and also add 1 to the count
variable).
Here’s what the stack will look like, just before the return:
In the above drawing we have a stack frame for main()
, and on top of
that a stack frame for limit()
. In the stack frame for limit()
I am
ignoring the loop variable, i
. Also, since the for
loop has already
finished, the count
variable has been reassigned twice: first to 1
,
then again to 2
.
Here’s the main point of this stack diagram: when the limit()
function
ends and we return to main()
, what will the print(data)
line in main()
display? Will it display the original list ([20,88,-13,200,301,199]
)
or the updated list ([20,88,0,200,255,199]
)???
If you look at the stack diagram, you should see that data
points to
the modified list, so when we print data
from main()
, we’ll see
the modified list.
Because python lists are mutable, we can change items in a list, in a function, and see those changes even after the function is finished.
Notice that the limit()
function doesn’t have to return L
, yet we
can still see the effects of changing L
back in main()
. This is
often called a side effect, when a function changes data, and the
effects are seen back in main()
.
tic-tac-toe
Let’s look at an example of the above, in a real program. Suppose we want to create a terminal-based tic-tac-toe game:
$ python3 ttt.py | | 0 1 2 ----- | | 3 4 5 ----- | | 6 7 8
O's turn: 4 | | 0 1 2 ----- |O| 3 4 5 ----- | | 6 7 8
X's turn: 2 | |X 0 1 2 ----- |O| 3 4 5 ----- | | 6 7 8
O's turn: 8 | |X 0 1 2 ----- |O| 3 4 5 ----- | |O 6 7 8
X's turn:
Here’s some starter code that creates the board (a python list) and displays the board as a tic-tac-toe board (3x3 rowsxcolumns):
"""
terminal-based tic-tac-toe game
Spring 2020
"""
from random import *
def main():
board = list(" ") # 9 spaces, initial empty board
display(board)
player = choice("XO")
def display(board):
"""display 9-item python list as tic-tac-toe board"""
for i in [0,3,6]:
print("%s|%s|%s %s %s %s" % (board[i],board[i+1],board[i+2],i,i+1,i+2))
if i != 6:
print("-"*5)
print()
main()
So the board
variable is just a python list of 9 single-space characters:
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
And the player
variable is either an "X" or an "O" character
to start. Your job is to write a turn(board,player)
function
that, given the board and the player character, asks the user
which spot to mark (0-8), and then changes the board accordingly
(i.e., either puts an "X" or an "O" in the list).
Here’s what our main()
function should look like after
you write the turn(board,player)
function:
def main():
board = list(" ")
display(board)
player = choice("XO")
turn(board,player)
display(board)
Once you have that working, let’s add a while
loop to keep playing,
taking more turns:
def main():
board = list(" ")
display(board)
player = choice("XO")
done = False
while not done:
turn(board,player)
display(board)
# change whose turn it is
This loop uses the while not done
pattern, where done
is a simple boolean
variable. I find this useful when the condition to end the loop is
complex, and may depend on more than thing. For example, the game might
be over, or a draw, or the user just wants to quit early. What’s not
shown yet, is some branching options where done
gets set to True
,
like these pseudo-code examples:
if the game is over: done = True elif the user wants to quit: done = True else: keep going with the game...
Note: with the above main()
, and no way to really end the game,
you may have to use Ctrl-c
to kill the program.
Friday
-
Quiz 2
tic-tac-toe (again)
Here’s one way to write the while
loop for the tic-tac-toe turn(..)
function:
def turn(board,player):
"""get move for player, change the board"""
while True:
index = int(input("%s's turn: " % (player)))
if board[index] == " ":
board[index] = player
return
else:
print("That space is already taken...")
This just checks to make sure the space chosen isn’t already taken. Other checks we still need to add: make sure the enter an integer from 0-8, and make sure they don’t enter something invalid (like 'zebra').
The while True
syntax is just an easy way to get the loop going,
without having to worry about the condition. We want to keep looping if
they enter a space already take, or they enter a non-integer, or they
enter an integer that’s out of range. That’s a complicated condition to
write and get correct. Sometimes it is easier to just get the loop
going, then have some if\elif\else
branches that check for each
separate condition.
Here’s the same function, but with an additional check that the number entered is in range (still won’t work for input 'zebra'):
def turn(board,player):
"""get move for player, change the board"""
while True:
index = int(input("%s's turn: " % (player)))
if index < 0 or index > 8:
print("please enter a number from 0-8...")
elif board[index] == " ":
board[index] = player
return
else:
print("that spot is already taken")
In both of these examples the return
line is the only way out of the
loop. Once the return
statement is hit, the loop and the function
are done, and the program returns to main()
to continue with the rest
of the program.
Note: we are not actually returning anything, we’re just saying, "if you get here, we’re done, so stop this function and go back to main and carry on from there".
objects and methods
Both python strings and python lists are examples of objects. An object is just some data plus some methods (similar to functions) wrapped up together into one thing. We’ll learn more about objects as we go. For now we just want to learn the syntax of using them: object.method()
example
>>> course = "Introduction to Computer Science" >>> course.count("o") 4 >>> course.count("z") 0 >>> course.isalpha() False >>> course.upper() 'INTRODUCTION TO COMPUTER SCIENCE'
>>> grades = [88,90,78,99,93] >>> grades.sort() >>> print(grades) [78, 88, 90, 93, 99] >>> grades.append(75) >>> print(grades) [78, 88, 90, 93, 99, 75]
explanation/notes
The string methods used above are:
-
count()
— returns the number of occurrences of the given substring -
isalpha()
— returns True if all characters in the string are letters -
upper()
— returns an uppercase version of the string
There are many other str methods. In string objects, the data are the characters in the string.
The list methods used above are:
-
sort()
— sorts the list in place -
append()
— adds the given argument to the end of the list
In list objects, the data are the items in the list.
object.method()
syntax
Methods are always called with the dot syntax: object.method()
Again, methods are similar to functions, except they are tied to the type of object, and are called with this special syntax.
common string methods
Strings are objects in Python, and thus have methods that
we can invoke on them.
There are a lot of methods in the str
library for creating new
string objects from existing string objects and for testing properties
of strings. Keep in mind that strings are immutable!
Here are a few str
methods that may be particularly useful (run help(str)
in the python interpreter to see the full set):
str method | result | |
---|---|---|
upper() |
return copy of str converted to uppercase |
|
lower() |
return copy of str converted to lowercase |
|
isalpha() |
return True if string is all alphabetic characters |
|
isdigit() |
return True if string is all digits |
|
count(sub) |
return number of occurrences of sub |
|
index(sub) |
return index of first occurrence of sub |
|
strip() |
strip off leading and trailing whitespace |
|
split() |
split into list of "words" (see below) |
>>> S = "we LOVE cs" >>> S.upper() 'WE LOVE CS' >>> S.lower() 'we love cs' >>> S.isalpha() False >>> S.isdigit() False >>> S.count(" ") 2 >>> S.index("L") 3 >>> S.split() ['we', 'LOVE', 'cs'] >>> S = " we love cs " >>> len(S) 17 >>> S = S.strip() >>> len(S) 10 >>> print(S) we love cs
common list methods
Lists are also objects in Python, and thus have methods that
we can invoke on them. Here are a few that may be particularly useful
(run help(list)
in the python interpreter to see the full set):
list method | result | |
---|---|---|
append(item) |
add item to end of list |
|
insert(index,item) |
insert item at index |
|
extend(L1) |
add list L1 to original list |
|
sort() |
sort the list |
|
reverse() |
reverse the list |
|
count(item) |
return number of occurrences of item in list |
|
index(item) |
return index of first occurrence of item |
|
pop(index) |
remove and return item at index |
>>> L = list("ABCDEFG") >>> print(L) ['A', 'B', 'C', 'D', 'E', 'F', 'G'] >>> L.append("X") >>> print(L) ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'X'] >>> L.extend(["Y","Z"]) >>> print(L) ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'X', 'Y', 'Z'] >>> L.reverse() >>> print(L) ['Z', 'Y', 'X', 'G', 'F', 'E', 'D', 'C', 'B', 'A'] >>> L.sort() >>> print(L) ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'X', 'Y', 'Z'] >>> L.count("E") 1 >>> L.index("E") 4 >>> L.pop(4) 'E' >>> print(L) ['A', 'B', 'C', 'D', 'F', 'G', 'X', 'Y', 'Z'] >>> L.insert(1,"hello") >>> print(L) ['A', 'hello', 'B', 'C', 'D', 'F', 'G', 'X', 'Y', 'Z']
To see the full documentation for both the str
and list
classes:
$ python3 >>> help(str) >>> help(list)