1 Getting Ready For Final Exam
1.1 Skills
1.1.1 Python Modules
1.1.2 Control Structures
1.2 In-class Exercise
1.3 Exceptions
1.4 Clicker Done Now
1.5 Python into MATLAB
1.6 Rosetta Stone: Fizz Buzz in C
1.7 Rosetta Stone: Fizz Buzz in Java
1.8 In-Class Exercise
1.9 Version : 2014/ 03/ 04

CS 110X Mar 03 2014

Lecture Path: 23
Back Next

Expected reading: 215-219
Clicker: Final Assessment

There is no exception to the rule that every rule has an exception.
James Thurber

1 Getting Ready For Final Exam

Today’s lecture may be ... interesting. I lost my voice Saturday and I hope it will be back by Monday 10AM. If not, we will have in-class handouts to go over... and this lecture slide to read :)

1.1 Skills

We are continuing the process of reviewing the core skills that I used when designing this course.

1.1.1 Python Modules
1.1.2 Control Structures

1.2 In-class Exercise

The following question is a suitable one for the final exam.

You are given a list of integers and you want to determine if it represents a sequence of increasing values that reaches a maximum value, after which all values are decreasing.

>>> isUpDown([1, 4, 9, 7, 2]) True >>> isUpDown([2, 4, 6, 3, 9]) False

Get started and we’ll solve in open session. As it turns out, this problem is more challenging than what you would find on the exam.

After class, I posted the following solution(s):

def isUpDown(values): """ Determine if list increases to a maximum and then decreases. Assumed to have at least three elements. """ peak = max(values) peakIndex = values.index(peak) left = values[:peakIndex+1] right = values[peakIndex:] # To be completely correct, must handle bad cases right now. # What if maximum is first or last? Then can’t go up and down if len(left) == 1 or len(right) == 1: return (False) # what if left is not increasing? for idx in range(len(left)-1): if left[idx] >= left[idx+1]: return False # what if right is not decreasing? for idx in range(len(right)-1): if right[idx] <= right[idx+1]: return False return True

There is another way to solve this problem, which uses a single while loop.

def isUpDownToo(values): """ Determine if list increases to a maximum and then decreases. Assumed to have at least three elements. """ if values[0] >= values[1]: return False if values[-2] <= values[-1]: return False ascending = True i = 0 while i < len(values)-1: # If in the ascending part, convert to descending as needed if ascending: if values[i] >= values[i+1]: ascending = False else: # only in descending can return False if increasing again if values[i] <= values[i+1]: return False i += 1 # original assumpion is correct return True

In both of these cases, the important concepts to consider are the overall approach towards solving the problem. In the first case, the developer had an "Aha!" moment and focused on the maximum value. In the second case, the developer focused on the need to detect when an increasing sequence turned into a descending sequence.

1.3 Exceptions

The final concept to be taught in this course is the notion of an Exception, which you have can find from the reading for today.

Throughout this course I have assumed at nearly every opportunity that user input would be appropriate. Clearly the real world cannot be so easily constrained. So what does a program do when it receives unexpected input? For example, the following program requests to get two integer values to represent the low and high points of a range:

def retrieveRange(): """ Retrieve low and high integers from user representing a range [low, high) Return as tuple (low, high) """ low = input ("Enter low integer: ") high = input ("Enter high integer: ") return (low, high)

As this code stands, there are multiple problems that could happen when this program is run: (a) what if the user presses RETURN and enters an empty value? (b) what if user enters in a string? (c) what if user enters in a low value which is >= high?

Let’s handle the easy case first:

def retrieveRange(): """ Retrieve low and high integers from user representing a range [low, high) Return as tuple (low, high) """ while True: low = input ("Enter low integer: ") high = input ("Enter high integer: ") if low < high: return (low, high) print ("Please enter [low, high) with integer values")

This code will repeat until the user enters in integers that satisfy the basic requirements of [low, high).

To deal with the other problems, we have to give the user a chance to try and enter in the appropriate values, but if they are incorrect for any reason, we want to perform some alternative action.

To set up the proper structure, consider the if statement.

... if x > 0: # Potentially unsafe print ("Square root is: " + str(x ** 0.5)) else: print ("Unable to take square root of negative number!")

Here the x > 0 acts as a guard to prevent the square root of a negative number. An alternate approach to the above is:

... try: # Potentially unsafe print ("Square root is: " + str(x ** 0.5)) except: print ("Unable to take square root of negative number!")

In this case, the user has decided not to determine the possibly numerous ways in which the potentially unsafe code could actually be unsafe. Instead, the code simply "tries" to execute the potentially dangerous code, and if an error occurs, the except body of statements is given the responsibility of "handling" the issue.

def retrieveRange(): """ Retrieve low and high integers from user representing a range [low, high) Return as tuple (low, high) """ while True: try: low = input ("Enter low integer: ") high = input ("Enter high integer: ") if low < high: return (low, high) except: print("Invalid user input. Integer values only!") print ("Please enter [low, high) with integer values")

The above looks really good; Try it out! When you enter integer values appropriate to the requirements, it returns the tuple (low, high). However, if low >= high then the function remains in the while loop until the user enters in the values.

If the user enters in a string, or presses ENTER for an empty input, the program detects this exceptional case, and immediately executes the except: exception handler, reminding the user to enter appropriate information.

There is just one thing wrong (minor point) with this particular solution. If you try to stop the program by using the "Control-C" interruption technique, Python will consider that action to be handled by the exception handler. You can accept this behavior, because you can restart the Python terminal from the Menu bar.

But you can consider an alternative:

def retrieveRange(): """ Retrieve low and high integers from user representing a range [low, high) Return as tuple (low, high) or () if terminate input """ while True: lowS = raw_input ("Enter low integer: ") try: low = int(lowS) except: print ("’" + lowS + "’ is invalid input for low.") return () highS = raw_input ("Enter high integer: ") try: high = int(highS) except: print ("’" + highS + "’ is invalid input for high.") return () if low < high: return (low, high) print ("Please enter [low, high) with integer values")

This code now chooses to use raw_input because of the extra control it gives the programmer. Now, should the user enter in an invalid piece of data for either low or high, the code immediately returns () which is a signal that there was an error. This code has the added benefit of being able to report back to the user exactly why the input was invalid.

1.4 Clicker Done Now

TBA

1.5 Python into MATLAB

Do you want to see how you can transition into MATLAB from Python? You can start by reviewing some past MQPs.

Here is a sample Distinguishing Between Asthma And Pneumonia Through Automated Lung Sound Analysis. Algorithm is sketched out on page 69. Given what you currently know about programming in Python, you should be able to take a stab at understanding what is going on here.

function crackle_ratio = cracklefind(y,Fs) NFFT = 2048; WINDOW = 128; MINFRACTION = 0.6; MAXFRACTION = 1.0; MINFREQ = 200; MAXFREQ = 1500; MINTIME = 0; MAXTIME = 9.5; % Defining width of the crackle widthMIN=1; widthMAX=3; [B,Freqs,Times] = specgram(y,NFFT,Fs,WINDOW); MINFREQ = sum(Freqs <= MINFREQ); MAXFREQ = sum(Freqs <= MAXFREQ); MINTIME = sum(Times <= MINTIME); MAXTIME = sum(Times <= MAXTIME); %image is B(freq,time) specdata = (20*log10(abs(B)))’; howbig = size(specdata); baseline = -20; BWdata = (specdata > baseline); line = (mean(BWdata(MINTIME:MAXTIME,MINFREQ:MAXFREQ),2) > MINFRACTION)’ .* ... (mean(BWdata(MINTIME:MAXTIME,MINFREQ:MAXFREQ),2) < MAXFRACTION)’; newline = zeros(size(line)); ones=0; % ones counter for i=1:size(line,2) % increment the counter of pre-crackles if line(i) ones = ones + 1; end % a crackle is defined as a set width of 1s followed by a 0 if (line(i) == 0) if (ones >= widthMIN) & (ones <= widthMAX) newline(i) = 1; end ones=0; end end crackle_ratio = mean(newline) end

1.6 Rosetta Stone: FizzBuzz in C

It can be instructive to see what programming skills translate from one language to another. What can you infer from the following image:

Figure 1: Typical Greek Sign

Likely you can work out more than you might expect. Of course, you can only undesrstand this image because you know what a street sign is in your own language. You know that the triangle tips on the outside are directional indicators. Surely the color reflects importance, as well as the vertical arrangement. See? You know more than you realize.

So let’s show an implementation of FizzBuzz in the C programming language.

#include <stdio.h> #include <stdlib.h> #include <string.h> char **fizzBuzz(int low, int high) { char **result = calloc (high-low+1, sizeof(char *)); for (int idx = low; idx <= high; idx++) { if (idx % 15 == 0) { result[idx-low] = "Fizz Buzz"; } else if (idx % 3 == 0) { result[idx-low] = "Fizz"; } else if (idx % 5 == 0) { result[idx-low] = "Buzz"; } else { char buf[10]; sprintf(buf, "%d", idx); result[idx-low] = strdup(buf); } } return result; } void main (int argc, char **argv) { char **words = fizzBuzz(1, 20); for (int idx = 0; idx < 20; idx++) { printf ("%d -> %s\n", (idx+1), words[idx]); } }

1.7 Rosetta Stone: FizzBuzz in Java

Now let’s try this example in Java.

java.util.*; public class FizzBuzz { static ArrayList<String> fizzBuzz(int low, int high) { ArrayList<String> result = new ArrayList<String>(); for (int idx = low; idx <= high; idx++) { if (idx % 15 == 0) { result.add("Fizz Buzz"); } else if (idx % 3 == 0) { result.add("Fizz"); } else if (idx % 5 == 0) { result.add("Buzz"); } else { result.add("" + idx); } } return result; } public static void main(String args[]) { ArrayList<String> words = fizzBuzz(1, 20); for (int idx = 0; idx < 20; idx++) { System.out.println("" + (idx+1) + " -> " + words.get(idx)); } } }

1.8 In-Class Exercise

When determining the atomic weight for a molecular compound you have to count individual elements. This becomes complicated with formula such as "C4H10" (for Isobutane). Write a Python function that expands "C4H10" into "CCCCHHHHHHHHHH"

For simplicity, assume that each chemical symbol is a single letter. This is too complicated for an exam, but we can use it as an example to discuss how to approach these kind of problems.

As it turns out, this example is too complicated to demonstrate in class. Here is the full implementation

Letters = ’ABCDEFGHIJKLMNOPQRSTUVWYXZ’ Digits = ’0123456789’ # C4H10 –> CCCCHHHHHHHHHH def expand(molecule): """Expand molecule into constituent elements""" # only 1 character? Can leave now if len(molecule) == 1: return molecule expanded = ” while len(molecule) > 1: symbol = molecule[0] # Two letters in a row? Means only 1 atom of the first # so advance if molecule[1] in Letters: expanded += molecule[0] molecule = molecule[1:] else: # Otherwise go to end of string or next atom for index in range(1,len(molecule)): if molecule[index] not in Digits: break # if we ran out of string, make end if (molecule[index] in Digits): index = len(molecule) # extract number and advance num = int(molecule[1:index]) expanded += molecule[0] * num molecule = molecule[index:] # there may well be a final single atom which we can’t forget! expanded += molecule return (expanded)

1.9 Version : 2014/03/04

(c) 2014, George Heineman