Learning Outcomes
    - Will know what a block of code is.
 
    - An understanding of if statements and boolean expressions used
        with them.
 
    - An ability to use if-else statements
 
    - An ability to use if-elif-else statements
 
    - An understanding of conditionals used with strings
 
    - Be familiar with boolean operators
 
    - Be familiar with boolean algebra, including DeMorgan's law
 
    - Be familiar with Truth tables
 
    - Understand the use of logical and to replace nested ifs
 
    - Experience use of formatted output
 
 
Control Structures
    - Control Structures allow different lines of code to be executed 
        depending on an evaluation of some expression.
 
    - The most common conditional structure is the if structure. 
 
    - An if structure contains an expression and a block or body of 
        code.
 
    - When the expression is true, the block of code associated with 
        the if statement is executed, otherwise it's skipped.
 
 
If statements and code blocks
If statements take the form :
>>> if True:
...     x = 1
...        y = 2
  File "<stdin>", line 3
    y = 2
    ^
SyntaxError: invalid syntax
>>>
 
Boolean Expressions
    - The <condition> part of the if statement represents a 
        boolean expression that is either true or false.
 
    - Expressions can be formulated using mathematical symbols to compare 
        different values.
 
| Python Symbol | 
Meaning | 
| < | 
is less than | 
| <= | 
is less than or equal to | 
| == | 
is equal to | 
| >= | 
is greater than or equal to | 
| > | 
is greater than | 
| != | 
is not equal to | 
    - When an expression is evaluated, it returns a boolean value in 
        Python
 
Examples:
>>> x = 6
>>> y = 4
>>> x < y
False
>>> x <= y
False
>>> x == y
False
>>> x >= y
True
>>> x > y
True
>>> x != y
True
   Like any other value in Python, variables can store boolean values.
>>> aBoolean = x > y
>>> print aBoolean
True
    Boolean values themselves are boolean expressions.
    Other data types can be converted to booleans by using 
        bool(value).
        
            - Integer or Float 0 become false, any other number 
                becomes true.
 
            - Empty strings become false, any other string becomes true.
 
            - Empty lists become false, any other list becomes true.
 
        
>>> bool(0)
False
>>> bool(1)
True
>>> bool(3)
True
>>> bool(-1)
True
>>> bool(0.0)
False
>>> bool(0.1)
True
>>> bool("")
False
>>> bool("hi")
True
>>> bool([])
False
>>> bool([1, 2])
True
 
If statement execution
    - When the condition of an if statement evaluates to true, the code 
        block for that if statement is executed, otherwise it is skipped.
 
>>> x = 6
>>> y = 4
>>> if x > y:
...     print "x is greater than y"
... 
x is greater than y
>>> if x <= y:
...     print "x is less than or equal to y"
... 
>>> 
    The conditional "x > y" tests whether x is a greater value than y 
        and if that is true, the string "x is greater than y" is printed.
 
if-else
>>> x = 6
>>> y = 4
>>> if x >= y:
...     print "x is greater than or equal to y"
... else:
...     print "x is less than y"
... 
x is greater than or equal to y
    If we swap the values of x and y and rerun the if-else we get:
>>> x = y
>>> y = x
>>> if x >= y:
...     print "x is greater than or equal to y"
... else:
...     print "x is less than y"
... 
x is greater than or equal to y
    Something went wrong!
    The problem was in our swap. When we set x to y, x took on the value 4.
        Then when we set y to x, it again set it to 4 so that the values were 
        equal
    To swap the values correctly, we'll need to use a temporary variable 
        to hold the old value of x
    Here's what we should have written : 
>>> x = 6
>>> y = 4
>>> temp = x
>>> x = y
>>> y = temp
>>> if x >= y:
...     print "x is greater than or equal to y"
... else:
...     print "x is less than y"
... 
x is less than y
    It turns out that even though most languages require the use of
        a third variable (conventionally called temp) to perform a swap, 
        Python has a short-cut built into the language for this called
        simultaneous assignment.
>>> x = 6
>>> y = 4
>>> x, y = y, x
>>> if x >= y :
...     print "x is greater than or equal to y"
... else:
...     print "x is less than y"
...
x is less than y
>>>
 
Example Program Using if-else
    - Let's write a program that analyzes the kinds of characters in a
        string
 
# Filename:    ifelse.py
# Written by:  Sue Evans 
# Date:        7/29/09
# Section:     All
# Email:       bogar@cs.umbc.edu
#
# This program illustrates the use of the if-else 
# structure working with strings that can contain 
# any of the printable characters.  It counts the 
# number of letters and other characters in the 
# input string.
import string
def main():
    # give an explanation of the program
    print
    print "This program counts the letters and other" 
    print "characters in a message."
    print
    
    # initialize the counters
    letters = 0
    others = 0
    
    # get a string from the user
    message = raw_input("Enter your message : ")
    # change the message to have only upper case letters
    allCaps = string.upper(message)
    # loop over the string counting letters and others
    for char in allCaps :
        # get ASCII value of char
        value = ord(char)
        
        # if char is a letter increment letters
        if value >= 65 :
            if value <= 90 :
                letters = letters + 1
            # make sure we get others > 90
            else :
                others = others + 1
        # otherwise increment others < 65
        else :
            others = others + 1
    # calculate total number of characters
    total = letters + others
    
    # print the results
    print 
    print "Total characters : ", total 
    print "Letters : ", letters 
    print "Other : ", others 
    print 
    
main() 
 
    - Let's run it.
 
linuxserver1.cs.umbc.edu[120] python ifelse.py
This program counts the letters and other
characters in a message.
Enter your message : This is my 1st message.
Total characters :  23
Letters :  17
Other :  6
linuxserver1.cs.umbc.edu[121]
    What if we want to count the number of digits too ?
        
        If the character isn't a letter, then it is either a digit
        or it's some other kind of character.  So inside the else
        part, we could put another if-else structure.  Let's try it : 
# Filename:    ifelse2.py
# Written by:  Sue Evans 
# Date:        7/29/09
# Section:     All
# Email:       bogar@cs.umbc.edu
#
# This program illustrates the use of the if-else 
# structure working with strings that can contain 
# any of the printable characters.  It counts the 
# number of letters, digits and other characters
# in the input string.
import string
def main():
    # give an explanation of the program
    print
    print "This program counts the letters, digits"
    print "and other characters in a message."
    print
    
    # initialize the counters
    letters = 0
    digits = 0
    others = 0
    
    # get a string from the user
    message = raw_input("Enter your message : ")
    # change the message to have only upper case letters
    allCaps = string.upper(message)
    # loop over the string counting character types
    for char in allCaps :
        # get ASCII value of char
        value = ord(char)
        
        # if char is a letter, increment letters
        if value >= 65 :
            if value <= 90 :
                letters = letters + 1
            # make sure we get others > 90 
            else : 
                others = others + 1 
        # otherwise      
        else : 
            # if it's a digit, increment digits 
            if value >= 48 :
                if value <= 57 :
                    digits = digits + 1
                # make sure we get others 58 - 64
                else :
                    others = others + 1
            #otherwise increment others < 48 
            else : 
                others = others + 1 
    # calculate total number of characters 
    total = letters + digits + others 
    # print the results 
    print 
    print "Total characters : ", total
    print "Letters : ", letters 
    print "Digits : ", digits 
    print "Other : ", others 
    print 
    
main() 
 
    Let's run this new version
linuxserver1.cs.umbc.edu[139] python ifelse2.py
This program counts the letters, digits
and other characters in a message.
Enter your message : This is the 2nd 201 message.
Total characters :  28
Letters :  18
Digits :  4
Other :  6
linuxserver1.cs.umbc.edu[140]
    What if we wanted to also count the number of spaces ?
        
        We'd have to put another if-else inside the innermost else.  This
        is getting tedious and confusing.  It's also true that the indentation
        is getting deeper and deeper as we add more kinds of characters to
        count ... There has to be a better way !
 
if-elif-else
>>> grade = float(input('Enter a numeric grade: '))
Enter a numeric grade: 87
>>> if grade >= 90.0:
...     print 'A'
... elif grade >= 80.0:
...     print 'B'
... elif grade >= 70.0:
...     print 'C'
... elif grade >= 60.0:
...     print 'D'
... else:
...     print 'F'
... 
B
    Using this technique, we can now also modify our previous 
        character-counting code with an if-elif-else control and 
        even add counting spaces to it.
# Filename:    ifelifelse.py
# Written by:  Sue Evans 
# Date:        7/29/09
# Section:     All
# Email:       bogar@cs.umbc.edu
#
# This program illustrates the use of the if-else 
# structure working with strings that can contain 
# any of the printable characters.  It counts the 
# number of letters, digits, spaces and other
# characters in the input string.
import string
def main():
    # give an explanation of the program
    print
    print "This program counts the letters, digits,"
    print "spaces and other characters in a message."
    print
    
    # initialize the counters
    letters = 0
    digits = 0
    spaces = 0
    others = 0
    
    # get a string from the user
    message = raw_input("Enter your message : ")
    # change the message to have only upper case letters
    allCaps = string.upper(message)
    # loop over the string counting character types
    for char in allCaps :
        # get ASCII value of char
        value = ord(char)
        
        # if char is a letter, increment letters
        if value >= 65 : 
            if value <= 90 :
                letters = letters + 1
            # make sure we get others > 90
            else :
                others = others + 1
        # if it's a digit, increment digits
        elif value >= 48 :
            if value <= 57 :
                digits = digits + 1
            # make sure we get others 58 - 64
            else : 
                others = others + 1
        # if it's a space, increment spaces 
        elif value == 32 : 
            spaces = spaces + 1 
        # if none of the above, increment others < 48
        else : 
            others = others + 1 
            
    # calculate total number of characters 
    total = letters + digits + spaces + others 
    
    # print the results 
    print 
    print "Total characters : ", total          
    print "Letters : ", letters 
    print "Digits : ", digits 
    print "Spaces : ", spaces     
    print "Other : ", others 
    print 
    
main() 
 
     When we run this new version we get : 
linuxserver1.cs.umbc.edu[137] python ifelifelse.py
This program counts the letters, digits,
spaces and other characters in a message.
Enter your message : This is the 3rd 201 lecture message.
Total characters :  36
Letters :  25
Digits :  4
Spaces :  6
Other :  1
linuxserver1.cs.umbc.edu[138]
 
Exercise:
The National Weather Service has 5 categories to describe hurricanes, 
   shown in the following chart:
| Category | 
Wind Speed | 
| 1 - Minimum | 
74 - 95 mph | 
| 2 - Moderate | 
96 - 110 mph | 
| 3 - Extensive | 
111 - 130 mph | 
| 4 - Extreme | 
131 - 155 mph | 
| 5 - Catastrophic | 
greater than 155 mph | 
Write Python code that will get the current wind speed from the user 
   and will print out if this is a hurricane and which category number it
   is.
HINT: Make use of the fact that there are no breaks in the values.
         
    There is no need to use the and or or operators.
 
String Conditionals
    - In Python, the operators in boolean expressions, aren't restricted
        to numbers.
 
    - Boolean operators can also work on strings and other more complex 
        types.
 
    - For strings the operators take on the following meanings : 
 
| Python Symbol | 
Meaning | 
| < | 
First string comes alphabetically before second | 
| <= | 
First string comes alphabetically before second or is equivalent to
    it | 
| == | 
The two strings are the same | 
| >= | 
First string comes alphabetically later than second or is equivalent to it
     | 
| > | 
First string comes alphabetically later than second | 
| != | 
Strings are not the same | 
    - Capitalized strings come before any lower case strings, since the
        ASCII value of capital letters have a smaller numeric value than those 
        of lower-case letters
 
        
    - Using these operators, we can run menus and handle other forms of 
        queries from users.
 
    - The example below prints the points value for a playing card.
 
>>> cardName = raw_input("Enter the card's name : ")
Enter the card's name : Queen
>>> if cardName == "Ace":
...     print 1
... elif cardName == "King":
...     print 13
... elif cardName == "Queen":
...     print 12
... elif cardName == "Jack":
...     print 11
... else:
...     print int(cardName)
... 
12
 
Boolean Algebra
    - You can use Boolean algebra to manage and reduce complex expressions.
        
 
    - The and and or operators distribute over each 
        other.
 
    
        - a or (b and c) == (a or b) and (a or c)
 
        - a and (b or c) == (a and b) or (a and c)
 
    
    - DeMorgan's Law distributes an outer 'not' by negating (not-ing) each 
        of the components and flipping ands to ors and ors to ands.
 
    
        - not(a or b) == (not a) and (not b)
 
        - not(a and b) == (not a) or (not b)
 
    
 
Character Counting Revisited
    - Now that we know about logical and, we can simplify
        our character-counting program.
 
    - When trying to determine if a character is a letter or a digit,
        we had the following code : 
 
        # if char is a letter, increment letters 
        if value >= 65 :  
            if value <= 90 : 
                letters = letters + 1 
            # make sure we get others > 90 
            else : 
                others = others + 1 
 
        # if it's a digit, increment digits 
        elif value >= 48 : 
            if value <= 57 : 
                digits = digits + 1 
            # make sure we get others 58 - 64 
            else :  
                others = others + 1 
 
    - We can simplify this code considerably by using and
 
        # if char is a letter, increment letters
        if value >= 65 and value <= 90 :
            letters = letters + 1
        # if it's a digit, increment digits 
        elif value >= 48 and value <= 57 :
            digits = digits + 1
 
    - Here's the new version of the character counter program
 
# Filename:    ifelifelse2.py
# Written by:  Sue Evans 
# Date:        7/29/09
# Section:     All
# Email:       bogar@cs.umbc.edu
#
# This program illustrates the use of the if-else 
# structure working with strings that can contain 
# any of the printable characters.  It also uses
# logical and.  The program counts the number of
# letters, digits, spaces and other characters in 
# the input string.
import string
def main():
    # give an explanation of the program
    print
    print "This program counts the letters, digits,"
    print "spaces and other characters in a message."
    print
    
    # initialize the counters
    letters = 0
    digits = 0
    spaces = 0
    others = 0
    
    # get a string from the user
    message = raw_input("Enter your message : ")
    # change the message to have only upper case letters
    allCaps = string.upper(message)
    # loop over the string counting character types
    for char in allCaps :
        # get ASCII value of char
        value = ord(char)
        
        # if char is a letter, increment letters
        if value >= 65 and value <= 90 :
            letters = letters + 1
        # if it's a digit, increment digits      
        elif value >= 48 and value <= 57 :
            digits = digits + 1
        # if it's a space, increment spaces
        elif value == 32 :
            spaces = spaces + 1
        # if none of the above, increment others    
        else :
            others = others + 1
            
    # calculate total number of characters
    total = letters + digits + spaces + others
    
    # print the results
    print
    print "Total characters : ", total           
    print "Letters : ", letters
    print "Digits : ", digits
    print "Spaces : ", spaces    
    print "Other : ", others
    print
    
main()
 
    - Since we've changed our code, we should run it again to make sure
        it still works.
 
linuxserver1.cs.umbc.edu[121] python ifelifelse2.py
This program counts the letters, digits,
spaces and other characters in a message.
Enter your message : This seems like the 100th time we've run this.
Total characters :  46
Letters :  33
Digits :  3
Spaces :  8
Other :  2
linuxserver1.cs.umbc.edu[122]
  
    It's also bothering me that our output looks sloppy, so before
        we put the character counter out to pasture, let's try to clean up
        the output.
    I think it would look better if all the numbers were lined up in
        a column and then have the total printed beneath the individual
        counts, like an addition problem.
    Our code for the output currently looks like : 
    # print the results
    print
    print "Total characters : ", total
    print "Letters : ", letters
    print "Digits : ", digits
    print "Spaces : ", spaces
    print "Other : ", others
    print
 
    Let's change it to : 
    # print the results
    print
    print "Letters :", letters
    print " Digits :", digits
    print " Spaces :", spaces
    print "  Other :", others
    print "            ---"
    print "  Total :", total
    print
 
    Let's see what the output looks like now.
linuxserver1.cs.umbc.edu[129] python format.py
This program counts the letters, digits,
spaces and other characters in a message.
Enter your message : Here's the 101st time !!!
Letters : 14
 Digits : 3
 Spaces : 4
  Other : 4
             ---
  Total : 25
linuxserver1.cs.umbc.edu[130]
    @&$#*&!!!
    We really wanted the 3 and the 4s in the units column, not the
        tens column.
    We can format our output as in this program snippet :
    # print the formatted results
    print
    print "Letters : %3d" % (letters)
    print " Digits : %3d" % (digits)
    print " Spaces : %3d" % (spaces)
    print "  Other : %3d" % (others)
    print "          ---"
    print "  Total :%4d" % (total)
    print
 
    In the print statement we can indicate that we want the value
        of a variable printed right-justified within a certain width.  The
        type of the variable also needs to be specified.
    The %3d being used in the string of the print statements 
        above means to print the value of an integer in base 10, decimal,-
        the d stands for decimal integer.  The 3 says the field
        width we want is 3.  Numbers are right-justified.
    Notice that I've moved the line to the left and have used %4d for
        the total.  I'm assuming that the user won't type an extremely 
        long message, but I'm allowing there to be up to 999 letters
        before the formatting will break.  If there were 999 letters, there
        probably will exist at least one other kind of character, which means
        the total will have to have a width of 4.
linuxserver1.cs.umbc.edu[135] python format2.py
This program counts the letters, digits,
spaces and other characters in a message.
Enter your message : It's the 1000th time !!!!!
Letters :  12
 Digits :   4
 Spaces :   4
  Other :   6
          ---
  Total :  26
linuxserver1.cs.umbc.edu[136]
    Success !