15-112 Lecture 3 (Wednesday, July 3, 2013)

Decision Making

Let's consider the code below:


#!/usr/bin/python

number = int(raw_input("number> "))
if (number > 5):
  print "Number is greater than 5."
print "Done!"


number = int(raw_input("number> "))
if (number > 5):
  print "Number is greater than 5."
else:
  print "Number is less than or equal to 5."
print "Done!"


number = int(raw_input("number> "))
if (number > 5):
  print "Number is greater than 5."
elif (number < 5):
  print "Number is less 5."
else:
  print "Number is equal to 5."
print "Done!"


word = raw_input ("word> ")
if (word == "hello"):
  print "Hello, back to you."
print "Done!"

word = raw_input ("word> ")
if (word != "hello"):
  print "Um, hi?"
print "Done!"
  

What are the take-aways?

Loops

Often times we want to do something more than once, until some condition is met. This is done with a "while loop", which is much like an automatically restartign "if statement". Essentially, it is structured like an "if statement", but loops back to the beginning, until the predicate is false. Since, like an "if statement", the predicate is tested before the body, if it is initally false, the body is never executed.

It is also very important that they body of the loop (or something) leads to the predicate eventually becoming false. Otherwise the loop will never end. Sometimes we do want "infinite loops" -- but these are very rarely a good thing. Usually they are a symptom of an unintended bug.

Let's see a few examples:


#!/usr/bin/python

index = 0
while (index < 10):
  print index
  index = index + 1

print ""

index = 0
while (index <= 10):
  print index
  index = index + 2

print ""

index = 10
while (index > 0):
  print index
  index = index - 2

#
# Infinite Loop below -- Caution
# See the problem? Note the predicate, the initial value of "index", and 
# how it is being changed with each iteration.
#
#print ""

#index = 10
#while (index <= 10):
#  print index
#  index = index - 2

  

Functions and Code Reuse

We've already made use of "functions" such as "len()". Now, let's take a deeper look and learn to write our own. Funtions are a way of taking a chunk of code, giving it a name, and clearly designating what inputs the code requires and what the resulting value is.

By creating functions from complex or reused code, we can make our programs more maintainable and more readable. If we have long, complex code, it can distract from the "Big picture" logic. To combat this, we can give it a meaningful name and "factor it out" into a function. Then, the detail is tucked away in the function, and the algorithmic, big-picture part of the code is clearer. It is like putting all of the tables and reference data at the back of a book, in an appendix, rather than in-line each and every time it is used.

And, if we have code that is used in mmore than one place, using functions enables us to write it only once and to "call" it from each use. this makes our program smaller. It means we have to type less, and it means that, if we ever need to revise the code, we only need to change it in one place -- rather than go hunting.

Let's see an example:


#!/usr/bin/python

def boxvolume(height,width,depth):
  volume = (height * width * depth)
  return volume

print "The volume of a 2x6x9 inch box is ",
print boxvolume(2,6,9),
print "cubic inches"

  

What are the take-aways?

A Quick Look at continue and break

A couple of minor features of the Python language that we might want to use from time-to-time are continue and break.

The example below illustrates continue, which causes control to immediately jump to the top of the loop, where the predicate is re-evaluated, without first running whatever code happens to be later in the loop.


def getUserSelection():
  VALID_OPTIONS = "ARPQ"
  MAX_ATTEMPTS = 3

  for attempt in range(3):
    # Print Menu
    print "--- Menu ---"
    print "A)dd a name"
    print "R)emove a name"
    print "P)rint all names"
    print "Q)uit"

    command = raw_input("command> ")

    # If the user supplies an empty line to raw_input
    # This raw_input() returns None
    # Just, FYI, None is equal to "", so we could test for that, too
    if (command == None):
      continue 

    if (command in VALID_OPTIONS):
      return command

print getUserSelection()
  

Not that, in the example below, break causes control flow to immediately leave the loop. It does not jump to the beginning, nor does it execute the code below -- it goes to the "bottom" of the loop, where execution would normally go once the loop finishes looping.


def addNumbers():
  # String containing legal number 
  NUMBERS = "0123456789"
  sum = 0

  while (True):
    number = raw_input("number> ")

    # This is really weak. We'll learn a better way, momentarily
    if ((len(number)!=1) or (number not in NUMBERS)):
      break     # Notice that we drop out of the loop here

    sum = sum+int(number)

  return sum

print addNumbers()
  

It is worth noting that code can get pretty tangled ocne you start making use of continue and break. The answer to the question, "Why am I looping?" becomes very hard to understand if the predicate isn't expressed in one place. And, the answer to the question, "What am I repeating?" becomes equally hard to understand if the answer is something different with each iteration. break often makes more of a mess than continue, because it undermines the loop's predicate as an invariant that describes the circumstances under which the loop is in effect. By contrast, continue complicates ones ability to understand which code will execute -- but doesn't undermine the loop's predicate, whcih is still tested before each repetition and the violation of which is still the exit condition for the loop.

If you find yourself using continue or break, you might want to consider if there is a better way of expressing the logic. Could these conditions be be better expressed as part of the predicate? Or by conditionally executing code within the loop, e.g. if statements? The answer isn't always, "Yes" -- but it is worth pausing to ask these types of questions.

Else for While Loops

In Python, unlike almost any other language, loops can have else clauses, just like if statements. This construct is rarely used -- and that is likely a good thing. There is rarely a reason for it.

A loop's else clause is executed if the loop exits -- execept upon a break. Consider the example below:


#!/usr/bin/python

count=1
while (count <=3):
  number = int(raw_input("number> "))
  
  if (number > 0):
    print number
  else:
    break

  count += 1
else:
  print "No negative numbers entered"