Week 4: indefinite loops, functions, string formatting

Announcements

  • due to the fire drill, Quiz 1 now scheduled for Wednesday

Monday

string formatting

motivation

Adding strings together and converting from ints or floats to strings gets tiresome for anything but simple strings. We could use the comma to print multiple items (e.g., print(x,y,z)) but that automatically adds a space, and doesn’t allow for precision. String formatting allows more control over the result, by specifying both width and precision.

Assume you have three variables:

num = 6
factor = 7
answer = num*factor

Using string concatenation, the print statement looks like this:

print(str(num) + " x " + str(factor) + " = " + str(answer))

Using string formatting, we avoid all of the str() calls:

print("%d x %d = %d" % (num,factor,answer))

And if the width of the integers changes (double or triple-digits), we can use the width specification to make everything line up! Without specifying the width, our times table looks like this:

6 x 1 = 6
6 x 2 = 12
6 x 3 = 18
6 x 4 = 24
6 x 5 = 30
6 x 6 = 36
6 x 7 = 42
6 x 8 = 48
6 x 9 = 54
6 x 10 = 60
6 x 11 = 66
6 x 12 = 72

Notice the 54 and the 60 don’t line up, because the 9 and the 10 have a different number of digits.

Using a width of two:

print("%2d x %2d = %2d" % (num,factor,answer))

makes the numbers in the table line up (all single-digit numbers are padded with one space):

6 x  1 =  6
6 x  2 = 12
6 x  3 = 18
6 x  4 = 24
6 x  5 = 30
6 x  6 = 36
6 x  7 = 42
6 x  8 = 48
6 x  9 = 54
6 x 10 = 60
6 x 11 = 66
6 x 12 = 72

syntax

There are three format-specifiers we normally use:

%f for floats
%s for strings
%d for (decimal) integers (I think %i also works for ints)

In the print statement, the format-specifiers get replaced by data, which is given at the end, like this:

print("%d x %d = %d" % (num,factor,answer))

In the above example, the first %d is replaced by the data stored in the variable num, the second is replaced by factor, and the third by answer.

Each format-specifier has an optional width, so, for example, you could print out 8-character usernames with %8s, padding the usernames that have less than 8 characters (like jknerr1) with spaces.

Floats have an additional precision specifier, so %7.2f means a width of 7 (including the decimal point), with 2 digits of precision (after the decimal).

examples

Making usernames line up:

>>> unames = ["ewu1","jtaylor5","dpike1","craty1","wchou1"]
>>> for name in unames:
...   print("%8s" % (name))
...
    ewu1
jtaylor5
  dpike1
  craty1
  wchou1

Making numbers line up, with 2 digits of precision:

>>> nums = [3.14159, 145.7, 2, 2000, 2.1234567]
>>> for num in nums:
...   print("Cost of item: $%7.2f" % (num))
...
Cost of item: $   3.14
Cost of item: $ 145.70
Cost of item: $   2.00
Cost of item: $2000.00
Cost of item: $   2.12

your turn!

Suppose you have parallel lists, meaning the first item in the first list corresponds to the first item in the second list, and so on. Here are parallel lists for elements and their atomic masses:

def main():
    elements = ["H","He","Li","Be","B","C"]
    amass = [1.0079,4.0026,6.941,9.0122,10.811,12.0107]

    for i in range(len(elements)):
        print(elements[i],amass[i])

main()

Currently they print out OK, but things don’t quite line up:

$ python3 periodictable.py
H 1.0079
He 4.0026
Li 6.941
Be 9.0122
B 10.811
C 12.0107

If you run update21 you should get the above periodictable.py file in your w04-whileloops directory. Add string formatting to the print() statement to make everything line up like this:

$ python3 periodictable.py
 H  1.0079
He  4.0026
Li  6.9410
Be  9.0122
 B 10.8110
 C 12.0107

Wednesday

Finally took Quiz 1! We also took our first look at functions!!

motivation

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.

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!

another example

Next we wrote the allCaps() function, which returns a boolean value:

def main():
    result = allCaps("HELLO")
    print(result)
    result = allCaps("hello")
    print(result)

def allCaps(string):
    """return True if string is all CAPs, False otherwise"""
    caps = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    for ch in string:
        if ch not in caps:
            return False

    # if we get here, past the for loop
    return True

main()

Again, the parameter string is assigned to whatever the argument is in the call to allCaps() in main(). In the first call, string will be "HELLO", and the function should return the value True.

In the allCaps() function, note how we can return as soon as any letter in the string is not in caps. We don’t need to check any further if we find a letter that is not in caps. If we make it past the for loop, then we know the string must be all capital letters, so we can then return a True value.

Friday

We reviewed the q1.py file you got when running update21. Also note the use of functions in that file!

Also take a look at this example of another function, with labels for the argument, parameter, and return value:

args and params image

Here’s an example of the running program, where the user enters "hello, 123":

$ python3 isdigit.py
Please enter a string: hello, 123

h is a digit? False
e is a digit? False
l is a digit? False
l is a digit? False
o is a digit? False
, is a digit? False
  is a digit? False
1 is a digit? True
2 is a digit? True
3 is a digit? True

valid user input

Starting with the rock papaer scissors code I gave you in the w04 directory (rps.py), let’s add some code to get the input from the user and make sure it’s valid input.

Here’s what we currently have:

user = input("rock, paper, or scissors? ")

That works, unless the user makes a typo or types in something crazy (like "zebra"). We could add more code to main(), but a better approach is to write a new function, and have that function take care of making sure the user enters a valid choice.

Here’s the start of the function:

def getInput():
    """get user input, make sure it is valid"""
    answer = input("rock, paper, or scissors? ")
    options = ["rock", "paper", "scissors"]

Now that I have the user’s answer, I want to make sure it is one of the valid options in my list. If it is, I return the answer, but if it isn’t, I want to print a message and ask again. This sounds like an if statement, but you don’t know if they will need more than one try, so we’ll use a while loop.

Here’s one way to write the function:

def getInput():
    """get user input, make sure it is valid"""
    answer = input("rock, paper, or scissors? ")
    options = ["rock", "paper", "scissors"]
    while answer not in options:
        print("That's not a valid option!!")
        answer = input("rock, paper, or scissors? ")
    return answer

Which says, as long as they give invalid input, keep printing the error message and asking again.

Here’s what the output looks like:

rock, paper, or scissors? yes
That's not a valid option!!
rock, paper, or scissors? zebra
That's not a valid option!!
rock, paper, or scissors? ROCK
That's not a valid option!!
rock, paper, or scissors? rock
I chose scissors

your turn!

See if you can write the countletter(letter,phrase) function shown in the file countletter.py:

$ cat countletter.py
"""
practice with functions...write the countletter(letter,phrase) function
"""

def main():
    phrase = input("enter a phrase: ")
    letter = input("enter a letter: ")
    result = countletter(letter,phrase)
    print("Number of %s's: %d" % (letter, result))

# add your countletter() function here!!

main()