Fall 2011 CSCI 220 Week 9

= Decision Structures =

Simple Decisions
Often it is necessary to alter the sequential flow of a program.

Consider our old temperature program: def main: celsius = eval(input("What is the Celsius temperature?")) fahrenheit = 9/5*celsius + 32 print("The temperature is",fahrenheit, "degrees fahrenheit.")

main

What if we want to print a message about extreme temperatures? Say temperatures above 90 degrees Fahrenheit or less than 30 degrees.

What we need is a method for our computers to make decisions (see Figure 7.1)?

In Python this program will look like: def main: celsius = eval(input("What is the Celsius temperature?")) fahrenheit = 9/5*celsius + 32 print("The temperature is",fahrenheit, "degrees fahrenheit.") # Print warning for extreme temps if fahrenheit > 90: print("It's really hot out there. Be Careful!") if fahrenheit < 30: print("Brrrr. Be sure to dress warmly!")

main

You can see that the Python if statement is used to implement the decision. The form of if statements are: if :

Forming Simple Conditions
Specifically, a conditional looks like:

is short for relational operator, which is a fancy name for mathematical concepts like "less than" or "equal to." There are six relational operators in Python:

Conditions may compare either numbers or strings. When comparing strings, the ordering is lexicographic, which means that strings are put in alphabetic order according to the underlying Unicode values. This means all uppercase Latin letters come before lowercase equivalents (e.g., Bbbb comes before aaaa).

These conditions are actually a type of expression, called a Boolean expression, after George Boole, a 19th century English mathematician. When a boolean expression is evaluated it produces a value of either true or false. In some older languages, you will see 1 or 0 to represent true and false, respectively. In Python, you will see True and False.

>>> 3 < 4 True >>> 3*4 < 3+4 False >>> "hello" == "hello" True >>> "hello" < "hello" False >>> "Hello" < "hello" True

Example: Conditional Program Execution
In the past, we have called some Python programs libraries, while we have called others programs or scripts. Sometimes we want to create a sort of hybrid module that can be used both as a stand-alone program and as a library. So far, most of our programs have had a line at the end of the program that invokes the main function. Since Python evaluates the lines of a module during the import process, our current programs also run when they are imported. Generally, it is nicer to have modules not to run when they are imported. In a program designed to be either imported (without running) or run directly, the call to main at the bottom must be made conditional. A simple decision should do the trick. How could we use the following:

>>> import math >>> math.__name__ 'math' >>> __name__ '__main__'

Answer: if __name__ == '__main__': main

Two-Way Decisions
Take our quadratic program as an example: import math

def main: a,b,c = eval(input("Please enter the coefficients (a, b, c): ")) discRoot = math.sqrt(b*b - 4*a*c) root1 = (-b + discRoot) / (2*a) root2 = (-b - discRoot) / (2*a)

print("The solutions are:",root1,root2)

main

As we noted earlier in the semester, the program crashes when there are no real roots. This happens when discRoot is less than 0. We can now use conditionals to fix our program, so it doesn't crash:

import math

def main: a,b,c = eval(input("Please enter the coefficients (a, b, c): ")) discrim = b*b - 4*a*c if discrim >= 0: discRoot = math.sqrt(discrim) root1 = (-b + discRoot) / (2*a) root2 = (-b - discRoot) / (2*a) print("The solutions are:",root1,root2)

main

What happens when you enter in 1,2,3?

This is almost worse than the previous version, because it does not give users any indication of what went wrong. We could add another decision:

import math

def main: a,b,c = eval(input("Please enter the coefficients (a, b, c): ")) discrim = b*b - 4*a*c if discrim >= 0: discRoot = math.sqrt(discrim) root1 = (-b + discRoot) / (2*a) root2 = (-b - discRoot) / (2*a) print("The solutions are:",root1,root2)

if discrim < 0: print("The equation has no real roots!")

main

But this doesn't feel right. We have programmed a sequence of two decisions, but the two outcomes are mutually exclusive. If discim >= 0 is true then discrim < 0 must be false. We have two conditions, but there is really only one decision. In Python, a two-way decision can be implemented by attaching an else clause onto an if clause. The result is called an if-else statement.

if : else:

See Figure 7.3.

We can now use this to write a more elegant solution:

import math

def main: a,b,c = eval(input("Please enter the coefficients (a, b, c): ")) discrim = b*b - 4*a*c if discrim >= 0: discRoot = math.sqrt(discrim) root1 = (-b + discRoot) / (2*a) root2 = (-b - discRoot) / (2*a) print("The solutions are:",root1,root2) else: print("The equation has no real roots!")

main

Multi-Way Decisions
The newest version of the quadratic solver is certainly an improvement, but it still has some problems:

Try to run the program using 1,2,1.

It prints "The solutions are: -1.0 -1.0." This is technically correct, but it might be confusing to some users. The double-root situation occurs when discrim is exactly 0. One way to code this is to use two if-else statements: import math

def main: a,b,c = eval(input("Please enter the coefficients (a, b, c): ")) discrim = b*b - 4*a*c if discrim >= 0: discRoot = math.sqrt(discrim) if discrim == 0: root = -b / (2*a) print("There is a double root at",root) else: root1 = (-b + discRoot) / (2*a) root2 = (-b - discRoot) / (2*a) print("The solutions are:",root1,root2) else: print("The equation has no real roots!")

main

Once again, we have a working solution, but the implementation doesn't feel quite right. We have finessed a three-way decision by using two two-way decisions. Imagine if we needed to make a five-way decision using this technique. There is another way to write a multi-way decision in Python that preserves the semantics of the nested structure. The idea is to combine an else followed immediately by an if into a single clause called an elif (pronounced "ell-if" or "else-if").

if : elif : elif : ... else:

This form is used to set off any number of mutually exclusive code blocks. Here is our program now: import math

def main: a,b,c = eval(input("Please enter the coefficients (a, b, c): ")) discrim = b*b - 4*a*c if discrim > 0: discRoot = math.sqrt(discrim) root1 = (-b + discRoot) / (2*a) root2 = (-b - discRoot) / (2*a) print("The solutions are:",root1,root2) elif discrim == 0: root = -b / (2*a) print("There is a double root at",root) else: print("The equation has no real roots!")

main

Exception Handling
Sometimes programs become so peppered with decisions to check for special cases that the main algorithm for handling the run-of-the-mill cases seems completely lost. Rather than explicitly checking for the errors before they arise, we can have the program deal with them once they occur. This is done using exception-handling. Here is a version of the quadratic program using exception handling: import math

def main: try: a,b,c = eval(input("Please enter the coefficients (a, b, c): ")) discRoot = math.sqrt(b*b - 4*a*c) root1 = (-b + discRoot) / (2*a) root2 = (-b - discRoot) / (2*a)

print("The solutions are:",root1,root2) except ValueError: print("No real roots")

main

This is formally defined as:

try: except :

When Python encounters a try statement, it attempts to execute the statements inside the body. If they execute with an error, then it passes control to the except clause with a matching error type.

Try...except statements can be used effectively to handle a variety of different errors:

import math

def main: try: a,b,c = eval(input("Please enter the coefficients (a, b, c): ")) discRoot = math.sqrt(b*b - 4*a*c) root1 = (-b + discRoot) / (2*a) root2 = (-b - discRoot) / (2*a)

print("The solutions are:",root1,root2) except ValueError as excObj: if str(excObj) == "math domain error": print("No real roots") else: print("You didn't give me the right number of coefficients") except NameError: print("You didn't enter three numbers") except TypeError: print("Your inputs were not all numbers") except SyntaxError: print("Your input was not in the correct form. Missing comma?") except: print("Something went wrong, sorry!")

main

Study in Design: Max of Three
x1,x2,x3 = eval(input("Enter three values: "))

max = x1 if x2 > max: max = x2 if x3 > max: max = x3

How would you deal with 4 numbers? How would you deal with n numbers?