## 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

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

```>>> 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```

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:

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))