CS 350 - Lab 09 "Python Functions & Coroutines"

resources:
python ref
python wiki
Python Tutorial
Python functions
examples/week09

Python was voted Programming Language of the Year in 2007 and 2010 by TIOBLE software Index and is currently the 8th most widely used language in the world. Python is a multi-paradigm language - object-oriented, functional, and procedural. This lab demonstrates two of Python's important features: 1) treating functions as first class objects and 2) coroutines.

IMPORTANT NOTE FOR vi ON SLEIPNIR

Spaces and indenting matter to Python. Check your settings with respects to expandtab, tabstop, and syntax on vi before saving your Python script. You can see all of your vi settings with the vi command:
:set all

PART I. Functions as First-Class Objects.
For this portion of the lab you will solve several problems that involve passing a function as a parameter to another function. If you have worked with C you know how messy it is to pass a pointer to a function as an argument. In C this functionality is a kludge since a C function differs fundamentally from a data type that you normally pass to a function. Treating a function as a first-class object means that you can pass a function to a function as seamlessly as you would pass any other object. You can think of a function as just another type of data.

Passing a function as an argument is the basis for callback function support in JavaScript, PHP and jQuery. Callbacks are supported in a number of web scripting languages primarily to add asynchronous program flow for event handlers. See callbacks.html for a simple example of this concept. Passing a function as an object is an inherent feature of functional languages beginning with Lisp. Anonymous (lambda) functions are applied to input as the input is on its way to another function application. Passing named and anonymous (lamdba) functions to another function is standard operating procedure in functional languages.

Before starting this lab, get an overview of the basics of Python in this test.py script. You should have a working knowledge of each line of code before starting the lab. Have the Python docs handy for in-depth explanation of the concepts. Python is a scripting language. Like lisp, you can also execute commands within the interpreter that you end up in by just typing python. See the readme for instructions on how to execute a script. Your job in this part of the lab is to add two Python functions to lab09.py. Each function will access lab09.data. The specifications for the functions given below. Start by copying the script and data file:

     $ cp /home/fac/melissa/public_html/cs350-f15/examples/week09/lab09.py  .
     $ cp /home/fac/melissa/public_html/cs350-f15/examples/week09/lab09.data .

Run the script. Make sure you understand everything in the existing code. Note: there is no error checking on input for the data file. For example, a blank line will crash the script. You can assume good input. Note: if you know what you are doing, you only need to add ten or so lines of code to the file. Specifications:
File lab09.data is structured like this:

      10
      15
      20
      0
      17

STEP 1. Add a function 'my_mean' to lab09.py that will take as arguments the 
list read in from lab09.data and a boolean function over x that returns true 
for x > 0. Function my_mean will return the mean for the values that meet the 
constraints of the boolean function. E.g.:

           my_mean (lambda x: x > 0, list)

will return the mean for the nonzero numbers in the list. In my_mean you will 
1) apply the boolean function to each number in the list; 2) count and sum 
the number of values for which B returns true; and 3) return the mean.

Hint: Don't forget to initialize sum and count as real numbers. My_mean is 
similar in concept to the function my_map that is given to you in lab09.py.

STEP 2. Add a function 'my_filter' to lab09.py that will take as arguments the 
list read in from lab09.data and a boolean function. My_filter will return 
the filtered list of values that meet the constraints of the function. E.g.:

           my_filter (lambda x: (x % 2) == 0, list)

will return a list that contains only even numbers.  

PART II. Coroutines in Python.
The communication between the caller and the callee in standard subroutines occurs once when the caller hands off control to the subroutine (jump) and once again when the subroutine hands control back to the caller (resume). The only place to enter a subroutine is at the top and the only place to exit it at the bottom. Coroutines, on the other hand, can have multiple points of entry and exit. Communication between the caller routine and the callee routine can occur often; e.g., the caller hands off control to the callee, the callee yields control back to the caller, the caller hands control back again, and so on and so forth. In this fashion coroutines can mimic the sort of back and forth that occurs between client/server applications.

Coroutines are supported in Python with calls to yield (which blocks waiting for a request) and next or send (which initiates a request with and without a message). Each time next or send is called the coroutine blocking on yield resumes execution.

Your job is to add two coroutines to lab09.py. Start by making some changes to a script that implements coroutines called coroutines.py. Grab the script here:

    $ cp /home/fac/melissa/public_html/cs350-f15/examples/week09/coroutines.py .

You will eventually integrate coroutines.py into lab09.py to make a single script. For now it is probably easier to work with coroutines.py and combine the two scripts later but it is up to you. Script coroutines.py uses Python lists. If the syntax is not intuitive, read the documentation on Python lists here. The script implements a variation on the producer/consumer algorithm. The producer in this case sends jobs to the consumer. The consumer waits for the producer to send a job, performs the job, and sends something back to the producer. Normally both producer and consumer yield to each other but in this algorithm only the consumer is blocking (waiting on the producer to give it the go-ahead to continue). Make these changes to coroutines.py:

STEP 1. In the existing program, main (all code in the script that is not in a subroutine) is acting as the producer. Modify the code so that the producer is its own function. The producer function will be called from main after the jobs data structure is initialized. The producer then starts the consumer.

STEP 2. Modify the script so that the data structure shared between the producer and the consumer is a list of 2-tuples instead of a list of strings (see test.py for an example of this). The list is shown here:

    jobs = [("wash",11),("dry",22),("fold",33)]

The first element in the 2-tuple is the job name. The second element in the 2-tuple is the job number. Modify all job requests to account for the different data type; i.e., the add request will look like this:
      
     printjob(s.send(("add","iron",44)))   

THREE. Finally, integrate your coroutines into lab09.py. Modify lab09.py to read in one additional cmdline argument in as an integer (see test.py for how to convert string to integer). Add the integer read in to the id value for each job; i.e., initialize your structure like this:

 jobs = [("wash",11+n),("dry",22+n),("fold",33+n)]

Sample output:
$ ./lab09.py lab09.data 99
PART I.
original list:  [10, 15, 20, 0, 17]
odds:    [15, 17]
squared: [100, 225, 400, 0, 289]
mean:    15.5
PART II.
Cmdline arg: 99
Producer starting.
Consumer starting.
wash,110 dry,121 fold,132 
ADD iron,44 
wash,110 dry,121 fold,132 iron,44 
REMOVE iron,44
fold,132 wash,110 dry,121 
Producer Done.

HOW YOUR LAB WILL BE GRADED

Your script must reside here:

  /home/stu/{username}/cs350/lab09/lab09.py

Your work will be graded by my script after the deadline.