In [1]:
import sys
print("Echo system status -- reset environment if kernel NOT 3.8...")
print(sys.executable)
print(sys.version)
print(sys.version_info)
! hostname
Echo system status -- reset environment if kernel NOT 3.8...
/opt/jupyterhub/bin/python3
3.8.2 (default, Jul 16 2020, 14:00:26) 
[GCC 9.3.0]
sys.version_info(major=3, minor=8, micro=2, releaselevel='final', serial=0)
atomickitty

Conditional Computations

I could not think of a good name for this section - decisions perhaps? Anyway, this section is all about using conditions - logical expressions that evaluate as TRUE or FALSE and using these results to perform further operations based on these conditions. All flow control in a program depends on evaluating conditions. The program will proceed differently based on the outcome of one or more conditions - really sophisticated "Artifical Intelligence" pro- grams are a collection of conditions and correlations (albeit very complex). Amazon knowing what you kind of want is based on correlations of your past behavior compared to other peoples similar, but more recent behavior, and then it uses conditional statements to decide what item to offer you in your recommendation items. Its uncanny, but ultimately just a program.

Comparisons

The most common conditional operation is comparison. If we wish to compare whether two variables are the same we use the == (double equal sign). For example x == y means the program will ask whether x and y have the same value. If they do, the result is TRUE if not then the result is FALSE. Other comparison signs are != does NOT equal, < smaller than,> larger than, <= less than or equal, and >= greater than or equal. Like Excel there are also three logical operators when we want to build multiple compares (multiple conditioning); these are and, or, and not. The and operator returns TRUE iff all conditions are TRUE. For instance 5 == 5 and 5 < 6 will return a TRUE because both conditions are true. The or operator returns TRUE if at least one condition is true. If all conditions are FALSE, then it will return a FALSE. For instance 4 > 3 or 17 > 20 or 3 == 2 will return TRUE because the first condition is true. The not operator returns TRUE if the condition after the not keyword is false. Think of it as a way to do a logic reversal.

The script below is a few simple examples of compares.

In [2]:
# compare
x = 7
y = 10
print("x= ",x,"y= ",y)
print("x is equal to y :",x==y)
print("x is not equal to y :",x!=y)
print("x is greater than y :",x>y)
print("x is less than y :",x<y)
x=  7 y=  10
x is equal to y : False
x is not equal to y : True
x is greater than y : False
x is less than y : True

The script below is a few simple examples of logical operators.

In [3]:
print("5 == 5 and 5 < 6 :",5 == 5 and 5 < 6)
print("4 > 3 or 17 > 20 or 3 ==2 ? :",4 > 3 or 17 > 20 or 3 ==2)
print("not5 == 5 :",not 5 == 5)
5 == 5 and 5 < 6 : True
4 > 3 or 17 > 20 or 3 ==2 ? : True
not5 == 5 : False

Block if statement

The if statement is a common flow control statement. It allows the program to evaluate if a certain condition is satisfied and to perform a designed action based on the result of the evaluation. The structure of an if statement is

if condition1 is met:
    do A
elif condition 2 is met:
    do b
elif condition 3 is met:
    do c
else:
    do e

The elif means "else if". The : colon is an important part of the structure it tells where the action begins. Also there are no scope delimiters like {} or () common in other programming tools. Instead Python uses indentation to isolate blocks of code. This convention is hugely important -- many other coding envi- ronments use delimiters (called scoping delimiters), but Python does not. The indentation itself is the scoping delimiter. The intent is for the code to be humanly readable for maintenance - you can use comment symbol # as a fake, searchable, delimiter but at times that itself gets cluttered.

The script below is an example that illustrates how the if statements work. The program asks the user for input. The use of input() will let the program read any input as a string so non-numeric results will not throw an error. The input is stored in the variable named userInput. Next the statement if userInput == "1": compares the value of userInput with the string "1". If the value in the variable is indeed "1", then the program will execute the block of code in the indentation after the colon. In this case it will execute

print ("Hello World")
print ("How do you do?")

Alternatively, if the value of userInput is the string "2", then the program will execute

print ("Snakes on a plane")

For all other values the program will execute

print ("You did not enter a valid number")
In [4]:
# block if
userInput = input("Enter the number 1 or the number 2")
if userInput == "1":
    print ("Hello World")
    print ("How do you do?")
elif userInput == "2":
    print("Snakes on a plane")
else:
    print("You did not enter a valid value")
Hello World
How do you do?

Inline if statement

An inline if statement is a simpler form of an if statement and is more convenient if you only need to perform a simple conditional task. The syntax is

do TaskA if condition is true else do TaskB

An example would be:

In [5]:
myInt = 3
num1 = 12 if myInt == 0 else 13
num1
Out[5]:
13

An alternative way is to enclose the condition in brackets for some clarity like

In [6]:
myInt = 3
num1 = 12 if (myInt == 0) else 13
num1
Out[6]:
13

In either case the result is that num1 will have the value 13 (unless you set myInt to 0).

for loop

We have seen the for loop already, but we will formally introduce it here. The loop executes a block of code repeatedly until the condition in the for statement is no longer true.

Looping through an iterable

An iterable is anything that can be looped over - typically a list, string, or tuple. The syntax for looping through an iterable is illustrated by an example. First a generic syntax

for a in iterable:
    print a

Notice our friends the colon : and the indentation.

Now a specific example

In [7]:
# set a list
MyPets = ["dusty","aspen","merrimee"]
# loop thru the list
for AllStrings in MyPets:
    print(AllStrings)
dusty
aspen
merrimee

We can also display the index of the list elements using the enumerate() function. Try the code below

In [8]:
# set a list
MyPets = ["dusty","aspen","merrimee"]
# loop thru the list
for index, AllStrings in enumerate(MyPets):
    print(index,AllStrings)
0 dusty
1 aspen
2 merrimee

For loops can be used for count controlled repetition, they work on a generic increment skip if greater type loop structure. The range function is used in place of the iterable, and the list is accessed directly by a name[index] structure

In [9]:
# set a list
MyPets = ["dusty","aspen","merrimee"]
how_many = len(MyPets)
# loop thru the list
for index in range(0,how_many,1):
    print(index,MyPets[index])
0 dusty
1 aspen
2 merrimee

while loop

The while loop repeats a block of instructions inside the loop while a condition remains true. The structure is

while condition is true:
    execute a
    ....

Notice our friends the colon : and the indentation again.

Try the code below to illustrate a while loop:

In [10]:
# set a counter
counter = 5
# while loop
while counter > 0:
    print ("Counter = ",counter)
    counter = counter -1
Counter =  5
Counter =  4
Counter =  3
Counter =  2
Counter =  1

The while loop structure depicted above is a structure that is referred to as "decrement, skip if equal" in lower level languages. The next structure, also a while loop is an "increment, skip if greater" Try this code:

In [11]:
# set a counter
counter = 0
# while loop
while counter < 5:
    print ("Counter = ",counter)
    counter = counter +1
Counter =  0
Counter =  1
Counter =  2
Counter =  3
Counter =  4

A few more variants include that same code, except the +=" operator replaces portions of the code.

In [12]:
# set a counter
counter = 0
# while loop
while counter < 5:
    print ("Counter = ",counter)
    counter += 1 #use the self+ operator
Counter =  0
Counter =  1
Counter =  2
Counter =  3
Counter =  4

And here we vary the condition slightly and use \<=" to capture the value 5 itself.

In [13]:
# set a counter
counter = 0
# while loop
while counter <= 5:
    print ("Counter = ",counter)
    counter += 1 #use the self+ operator
Counter =  0
Counter =  1
Counter =  2
Counter =  3
Counter =  4
Counter =  5

The infinite loop (a cool street address, poor programming structure)

while loops need to be used with care, it is reasonably easy to create infinite loops and we have to interrupt with a system call (possibly have to externally kill the process - easy if you are root and know how, a disaster if you have deployed the code to other people who are not programmers and system savvy) Here is an example of an infinite loop. (kill the process by halting the kernel when you run it)

In [14]:
# set a counter
counter = 5
counter = -4 #deactivate for infinite loop
# while loop
while counter > 0:
    print ("Counter = ",counter)
    counter = counter +1 # pretend we accidentally incremented instead of decremented the counter!

Infinite loops can be frustrating when you are maintaining a large (long) complex code and you have no idea which code segment is causing the infinite loop. Often a system administrator has to kill the process at the OS level.

The break instruction

Sometimes you may want to exit the loop when a certain condition different from the counting condition is met. Perhaps you are looping through a list and want to exit when you find the first element in the list that matches some criterion. The break keyword is useful for such an operation. For example run the following program

In [15]:
j = 0
for i in range(0,5,1):
    j += 2
    print ("i = ",i,"j = ",j)
    if j == 6:
        print("break from loop")
        break
print("value of j is: ",j)
i =  0 j =  2
i =  1 j =  4
i =  2 j =  6
break from loop
value of j is:  6
In [16]:
j = 0
for i in range(0,5,1):
    j += 2
    print ("i = ",i,"j = ",j)
    if j == 7:
        print("break from loop")
        break
print("value of j is: ",j)
i =  0 j =  2
i =  1 j =  4
i =  2 j =  6
i =  3 j =  8
i =  4 j =  10
value of j is:  10

Examining these two simple codes. In the first case, the for loop only executes 3 times before the condition j == 6 is TRUE and the loop is exited. In the second case, j == 7 never happens so the loop completes all its directed traverses. In both cases an if statement was used within a for loop. Such "mixed" control structures are quite common (and pretty necessary). A while loop contained within a for loop, with several if statements would be very common and such a structure is called nested control. There is typically an upper limit to nesting but the limit is pretty large -- easily in the hundreds. It depends on the language and the system architecture - suffice to say it is not a practical limit except possibly for AI applications.

The continue instruction

The continue instruction skips the block of code after it is executed for that iteration. It is best illustrated by an example.

In [17]:
j = 0
for i in range(0,5,1):
    j += 2
    print ("\ni = ", i , ", j = ", j) #here the \n is a newline command
    if j == 6:
        continue
    print (" this message will be skipped over if j = 6 ")
i =  0 , j =  2
 this message will be skipped over if j = 6 

i =  1 , j =  4
 this message will be skipped over if j = 6 

i =  2 , j =  6

i =  3 , j =  8
 this message will be skipped over if j = 6 

i =  4 , j =  10
 this message will be skipped over if j = 6 

When j==6 the line after the continue keyword is not printed. Other than that one difference the rest of the script runs normally.

The try, except instruction

The final control statement (and a pretty cool one for error trapping) is the try ..., except statement. The statement controls how the program proceeds when an error(called an exception) occurs in an instruction. The structure is really useful to trap likely errors (divide by zero, wrong kind of input) yet let the program keep running or at least issue a meaningful massage to the user.

The syntax is:

try:
    do something
except:
    do something else if ``do something'' returns an error

Here is a really simple, but hugely important example:

In [18]:
#MyErrorTrap.py
x = 12.
y = 12.
while y >= -12.:
    try:
        print ("x = ", x, "y = ", y, "x/y = ", x/y)
    except:
        print ("error divide by zero")
    y -= 1
x =  12.0 y =  12.0 x/y =  1.0
x =  12.0 y =  11.0 x/y =  1.0909090909090908
x =  12.0 y =  10.0 x/y =  1.2
x =  12.0 y =  9.0 x/y =  1.3333333333333333
x =  12.0 y =  8.0 x/y =  1.5
x =  12.0 y =  7.0 x/y =  1.7142857142857142
x =  12.0 y =  6.0 x/y =  2.0
x =  12.0 y =  5.0 x/y =  2.4
x =  12.0 y =  4.0 x/y =  3.0
x =  12.0 y =  3.0 x/y =  4.0
x =  12.0 y =  2.0 x/y =  6.0
x =  12.0 y =  1.0 x/y =  12.0
error divide by zero
x =  12.0 y =  -1.0 x/y =  -12.0
x =  12.0 y =  -2.0 x/y =  -6.0
x =  12.0 y =  -3.0 x/y =  -4.0
x =  12.0 y =  -4.0 x/y =  -3.0
x =  12.0 y =  -5.0 x/y =  -2.4
x =  12.0 y =  -6.0 x/y =  -2.0
x =  12.0 y =  -7.0 x/y =  -1.7142857142857142
x =  12.0 y =  -8.0 x/y =  -1.5
x =  12.0 y =  -9.0 x/y =  -1.3333333333333333
x =  12.0 y =  -10.0 x/y =  -1.2
x =  12.0 y =  -11.0 x/y =  -1.0909090909090908
x =  12.0 y =  -12.0 x/y =  -1.0

So this silly code starts with x fixed at a value of 12, and y starting at 12 and decreasing by 1 until y equals -12. The code returns the ratio of x to y and at one point y is equal to zero and the division would be undefined. By trapping the error the code can issue us a measure and keep running. Notice how the error is trapped when y is zero and reported as an attempted divide by zero, but the code keeps running.

Exercises

1)

Write a Python script that takes a real input value (a float) for x and returns the y value according to the rules below

\begin{equation} y = x for 0 <= x < 1 \\ y = x^2 for 1 <= x < 2 \\ y = x + 2 for 2 <= x < inf \\ \end{equation}
In [19]:
x = float(input("Enter a value for x"))
print("input =: ",x)
if x < 1:
    y=x
elif x>= 1 and x<2:
    y=x**2
elif x >=2:
    y=x+2
print("x and y = :",x,y)
input =:  2.0
x and y = : 2.0 4.0

2)

Modify the script to automatically print a table with x ranging from 0 to 5.0

In [20]:
# getX function
def getX(x):
    if x < 1:
        y=x
    elif x>= 1 and x<2:
        y=x**2
    elif x >=2:
        y=x+2
    return(y)
xlist = [0.,1.,2.,3.,4.,5.]
for i in xlist:
    print("x= ",i,"y =",getX(i))
x=  0.0 y = 0.0
x=  1.0 y = 1.0
x=  2.0 y = 4.0
x=  3.0 y = 5.0
x=  4.0 y = 6.0
x=  5.0 y = 7.0

3)

Modify the script again to increment the values by 0 to 0.5.

In [21]:
# getX function
def getX(x):
    if x < 1:
        y=x
    elif x>= 1 and x<2:
        y=x**2
    elif x >=2:
        y=x+2
    return(y)
xlist=[] # null list to take input
# build a list
xlist = [x/2+0.5 for x in range(-1,10,1)]
for i in xlist:
    if i <= 5.0:
        print("x= ",i,"y =",getX(i))
x=  0.0 y = 0.0
x=  0.5 y = 0.5
x=  1.0 y = 1.0
x=  1.5 y = 2.25
x=  2.0 y = 4.0
x=  2.5 y = 4.5
x=  3.0 y = 5.0
x=  3.5 y = 5.5
x=  4.0 y = 6.0
x=  4.5 y = 6.5
x=  5.0 y = 7.0

4)

Repeat Exercise 1 above, but include error trapping that: (a) Takes any numeric input and forces into a float. (b) Takes any non-numeric input, issues a message that the input needs to be numeric, and makes the user try again.11 (c) Once you have acceptable input, trap the condition if x < 0 and issue a message, otherwise complete the requisite arithmetic.

In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]: