Week 3: branching, booleans

Announcements

  • Lab 2 available now.

  • Quiz 1 Study guide available on course home page.

  • Quiz 1 Friday at start of class.

  • Wednesday Ninja session good time to review.

Monday

branching

Branching allows our programs to take different paths, depending on input. For example:

$ python3 grades.py
your numerical grade? 95
That's an A!
$ python3 grades.py
your numerical grade? 88
Good work.
That's a B.
$ python3 grades.py
your numerical grade? 72
C

Depending on what the user enters, we get different output.

Here’s one way to write this program:

  grade = float(input("your numerical grade? "))
  if grade >= 90:
      print("That's an A!")
  elif grade >= 80:
      print("Good work.")
      print("That's a B.")
  elif grade >= 70:
      print("C")
  elif grade >= 60:
      print("D")
  else:
      print("F")

This is a 5-way branch. In general, all branches start with the if condition: statement, but can have 0 or more elif condition: statements, and may or may not have and else: statement.

After each of the above statements (if, elif, and else) comes a code block: one or more indented lines. If the condition in any of the if\elif statements is True, the corresponding code block is executed, and no further statements are tested.

For example, in the above code, if the grade is 95, the first if condition is tested (grade >= 90), it evaluates to True, so the print("That’s an A!") code block is run. No other conditions are tested since the first one was True. If there were additional code after this 5-way branch, it would run immediately after the print("That’s an A!") statement.

Boolean Logic and Relational Operators

To evaluate conditions as either True or False, we need a new data type.

The Boolean or bool type can only hold two possible values: True or False. Note in Python, both of these values begin with an upper case letter and the values do not have quotes around them. The value "True" (with quotes) is a string, not a Boolean.

One way to generate a Boolean value is to use one of the relational operators listed below. For example, the operator < compares two variables or expressions left < right. If the value of left is smaller than right, the expression left < right evaluates to True, otherwise, the answer is False.

Python’s relational operators are:

Operator Meaning

<

less than

<=

less than or equal to

>

greater than

>=

greater than or equal to

==

equal to

!=

not equal to

Note that to check if two expressions are equal, you must use the ==, e.g., x == 7. Using x = 7 in Python has a different semantic meaning — it performs a variable assignment and stores the value of 7 in the container labeled x.

Here are some quick examples:

>>> name = "Jeff"
>>> name == "jeff"
False
>>> x = 5
>>> x > 10
False
>>> x <= 10
True
>>> x != 10
True

groundhog day

Let’s write this one:

$ python3 groundhog.py
did the groundhog see his shadow? yes
Bleh. 6 more weeks of winter.
$ python3 groundhog.py
did the groundhog see his shadow? no
Yay! Spring is coming early this year!

This is a simple two-way branch. One way to write this is:

  sawshadow = input("did the groundhog see his shadow? ")
  if sawshadow == "yes":
      print("Bleh. 6 more weeks of winter.")
  else:
      print("Yay! Spring is coming early this year!")

This works for the above examples. What happens if the user enters "zebra"? Here’s a second, slightly better version:

  sawshadow = input("did the groundhog see his shadow? ")
  if sawshadow == "yes":
      print("Bleh. 6 more weeks of winter.")
  elif sawshadow == "no":
      print("Yay! Spring is coming early this year!")
  else:
      print("huh?")

What happens if the user enters "Yes"?

your turn

How about this one?

$ python voting.py
Enter your age on election day: 20
You are eligible to vote

$ python voting.py
Enter your age on election day: 18
You are eligible to vote

$ python voting.py
Enter your age on election day: 2
You can't vote this year
You will need to wait 16 years

Wednesday

review from last time

"""
Kevin's voting example
"""

def main():
   age = int(input("Age on election day? "))
   if age >= 18:
      print("You are eligible to vote!")
   else:
      print("You can't vote this year.")
      howlong = 18 - age
      print("You will need to wait",howlong,"years")

main()

What if I want to add an additional note if they are close to being able to vote. For example, if they are within 2 years of being able to vote, let’s print out "Soon!".

def main():
   age = int(input("Age on election day? "))
   if age >= 18:
      print("You are eligible to vote!")
   else:
      print("You can't vote this year.")
      howlong = 18 - age
      print("You will need to wait",howlong,"years")
      if howlong <= 2:
          print("Soon!")

See how we can nest statements inside other code blocks?! Also see how the last if statement is by itself (i.e., no elif or else statements with it).

Logical Operators

In many programs, it’s convenient to ask compound questions or require multiple conditions be True before executing some code. In these cases, we can join two conditions together using a logical operator:

Operator Meaning

and

both boolean expressions must be true

or

at least one of the two boolean expressions must be true

not

negates the boolean value

Below is a truth table, where x and y represent Boolean values or expressions. For example, x could be age >= 18 and y could be status == "Yes". Each row should be read as follows: for the given Boolean values of x and y, what is the result of x and y, x or y, and not x:

x y x and y x or y not x

True

True

True

True

False

True

False

False

True

False

False

True

False

True

True

False

False

False

False

True

Let’s make the groundhog program a little better, and accept more than just "yes" or "no":

  sawshadow = input("did the groundhog see his shadow? ")
  if sawshadow == "yes" or sawshadow == "y" or sawshadow == "Yes":
      print("Bleh. 6 more weeks of winter.")
  elif sawshadow == "no" or sawshadow == "n" or sawshadow == "No":
      print("Yay! Spring is coming early this year!")
  else:
      print("huh?")

Substrings, in operator

A substring of a string is a portion of the string that appears contiguously. For example, "blue" is a substring of "blueberries". Python has some commands for accessing substrings.

The most relevant for us right now is the in operator (not to be confused with the in in for i in range(..)). The in (membership) operator takes a substring called the pattern and another string commonly called the target, and returns True if and only if the pattern appears as a substring in the target.

Sometimes, the pattern can be a single letter, but in general, the pattern can be any string.

>>> 'a' in "apples"
True
>>> 'b' in "apples"
False
>>> "vark" in "Aardvark"
True
>>> "bbrries" in "blueberries"
False

You can also use the in operator on lists:

>>> names = ["jeff","lauri","kevin","andy"]
>>> "JEFF" in names
False
>>> "jeff" in names
True
>>> L = [1,2,3,4,5]
>>> 99 in L
False
>>> 3 in L
True

Note that the in operator is just checking each item in the list, as we could do with a for loop:

FOUND = False
for name in names:
   if name=="jeff":
      FOUND = True
print(FOUND)

The above uses a boolean flag variable (FOUND), but does exactly the same thing as "jeff" in names.

Finally, here’s an even better version of our groundhog program, using the in operator:

    yes = ["yes","y","Yes","YES"]   # add as many as you want...
    no = ["no","n","No","NO"]
    sawshadow = input("did groundhog see his shadow? ")
    if sawshadow in yes:
        print("Bleh. 6 more weeks of winter.")
    elif sawshadow in no:
        print("Yay! Spring is coming early this year!")
    else:
        print("huh?")

your turn

Can you write this program, to count how many double letters are in a given string?

$ python3 doubles.py
text: hello
there is 1 double in 'hello'
$ python3 doubles.py
text: we love comp sci!
there are 0 doubles in 'we love comp sci!'
$ python3 doubles.py
text: mississippi
there are 3 doubles in 'mississippi'

Friday

Quiz 1!

Review of doubles.py.

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 w03-branching 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

Comparing Strings

We can compare string values just as we can compare ints. That is, we can use any relational operator on a pair of a strings.

"Aardvark" < "Baboon"

Strings are compared lexicographically (i.e., based on their sorted dictionary order). So, the above expression is True because Aardvark appears earlier in the dictionary.

Drilling down, Python actually compares the two strings character-by-character until it finds a difference. So, it will first compare A to B. It finds that they are different, and so it returns True. If the expression is:

"Apple" < "Applied"

Python first compares the As, then each p, then the ls, and finally stops at the next position since e and i are different. Since e comes first in the alphabet, it returns True.

What if we had:

"apple" < "APPLE"

What does Python do? To drill down even further, everything in the computer is represented numerically in binary (0s and 1s). So, even text is really represented as a series of numbers (positive integers, specifically). The encoding, or conversion, is known as Unicode. We can find the conversion using the ord() function:

$ python3
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> ord('A')
65
>>> ord('B')
66
>>> ord('Z')
90
>>> ord('!')
33

So to answer our question above, we need to compare the Unicode value of 'a' to 'A'. 'A' is a small Unicode value, so the expression is False.

We can also convert in the other direction - from a number to a character using the chr() function:

>>> chr(58)
':'
>>> chr(100)
'd'
>>> chr(75)
'K'