Assignment 2 - Call Frames

CS 1110 Spring 2019

Due to Gradescope by Thursday, February 21, 2019 @ 11:59pm

Solutions now available here.


Overview

In this written assignment you will diagram a few call frames. It should (hopefully) only take you two or three hours. So even if you are busy revising Assignment 1, you should be able to do this assignment as well. Please keep track of the actual time that you spent on this assignment for the survey.

“Diagramming” is what we do in the lecture notes where we draw boxes to represent function calls and folders to represent the objects. This is a visual representation of what is going on in the computer as it executes Python code. The purpose of this assignment is to solidify your understanding of how function calls work.

Learning Objectives

This assignment is design to help you understand the following concepts.

  • How to use call frames to model function execution.
  • The difference between a call frame and global space.
  • How folders represent Python's handling of objects.


Before You Get Started

Academic Integrity

With the exception of your Gradescope-registered partner, we ask that you do not look at anyone else's solutions, seek solutions online or from students of past offerings of this course, or show your answers to anyone else (except a CS1110 staff member) in any form whatsoever. This is in keeping with the the Academic Integrity Policy for CS 1110.

Partner Policy

You may do this assignment with one other person. When you upload your pdf to Gradescope, you will be given the option to add a person to the submisison. You may find these instructions helpful.

If you do this assignment with a partner, you must work together. It is against the rules for one person to complete the assignment and just add the other person to their submission without the other person having contributed. You should actively collaborate on this assignment. You may not collaborate with anyone other than your Gradescope partner.


Diagramming Conventions

Most of our diagramming conventions follow the Spring 2019 lecture notes. However, just to be safe (and because these conventions are different from the Fall offering of CS 1110), we will make our conventions explicit here. If you are someone who likes seeing worked examples, here is a small piece of code and three different corresponding diagrams: one done as a video by Prof. Bracy, one by Prof. Lillian Lee and one by Prof. Steve Marschner. Please note that the two drawings are from a semester that had a different convention about where to indicate the return value, but otherwise the notation is the same.

Diagramming Variables

A diagram for a variable should include the variable name and a box for the value. When we reassign a variable, old values are crossed out when they are replaced by new ones.

For example, here is a diagram for the variable b, currently holding the integer 2:

When writing the value, the only rule is to make sure it is unambiguous what the value is, and what type it is. Write floats with a decimal point, strings with quotes around them, and so forth.


Diagramming Objects

All values in Python are objects, but we only bother to draw an object separately from variables referring to it if the object is mutable (i.e., the contents of the folder can be changed). All of the base types — int, float, bool, and str — are immutable. So far the only mutable objects we have seen are those with named attributes, like those of type Point3.

When you diagram an object you should obey the following conventions:

  • The folder's identifier (ID, or true name) should be unique and go on the folder tab.
  • The type of the object should go at the top right of the folder.
  • The attributes should be listed inside the folder and diagrammed just like variables.

For example, a folder for an object of the type Point3 from class might look like this:


Diagramming Function Calls

Call frames show the temporary workspace where Python stores the variables used during a function call. Diagrams of frames should follow these conventions:

  • The top left corner is a box with the function name.

  • The top right counter is the program counter. This is next line of code to execute in the function.

  • When the call frame is first drawn, the instruction counter should contain the first line of executable code after the function header.

  • Local variables and parameters are diagrammed in the frame body; we do not distinguish between them.

  • Local variables are not added until the instruction counter reaches the line where they are first defined.

See the lecture notes for more details on how to create a call frame. We will always number the lines of code for you. For example, suppose we have the procedure

1  def print_name(last, first):
2      greet = 'Hello '+first+' '+last+'!'
3      print greet

If we execute the call print_name("Sotomayor","Sonia"), then the call frame starts out as follows:

The Evolution of a Call Frame

A call frame is not static. As Python executes the body of the function, it alters the contents of the call frame. For example, after we have executed line 2, the call frame now looks as follows:

This is not a new call frame; this is the same call frame shown at a different point in time. The call frame does not go away until the function call is completed. When we diagram a call frame, we are actually diagramming a sequence of changes to a single box to show how it changes over time as each line of code is executed.

As the program counter changes, we cross out the prevous value (keeping it legible). The diagram above refers to the call frame after we finish line 2. Once we finish line 3, the diagram for the call frame is as shown below.

(Note: the RETURN variable will be explained in the following section and is shown only for completeness purposes.) Note that there is no instruction counter (they are all crossed-out). That is because there are no more instructions to execute. However, the call frame is not yet deleted. To represent the deletion of the call frame, we cross out the frame:

Frames and Return Values

Fruitful functions have a return value. For example, consider the following function:

1  def get_feet(ht_in_inches):
2     feet = ht_in_inches // 12
3     return feet

When you execute a return statement, it makes a special variable called RETURN which stores the result of the expression. For example, suppose we have the function call get_feet(68). After executing line 2 of the code, above, the call frame is as follows:

return

When you execute the next line, the instruction counter becomes blank (because there is nowhere else to go), but you add the RETURN variable.

return-2

If you play with the Python Tutor, you will note that it always adds a RETURN variable, even for procedures (i.e., functions with no return statement). In this case it stores the value None into the RETURN variable. This was done above in the print_name("Sotomayor","Sonia") example above, copied here again for your convenience:

We follow this convention in class. Before a function's call frame is crossed out, you should have drawn the variable RETURN with its value in the box. Before a procedure's call frame is crossed out, you should have drawn the variable RETURN with the value None in the box.

Frames and Global Space

A function call is an expression. The call evaluates to the result of the return statement. If this call is used in an assignment statement, there is a way to copy from the RETURN variable to another place in memory, such as global space. For example, the call

  ft = to_feet(68)

will copy the final result (5) into the global variable ft.

If a function call is used in an assignment statement, then the value in the box is not changed until the step where you erase frame. In fact, if this is the first time that you are assigning the variable, you do not even create the box for the variable until the frame is erased.

If you were asked to draw the state of memory prior to the return statement on line 3 of get_feet, there would not yet be a variable y in the global space. It is only created after the call frame is erased.

Modules and Functions in Global Space

As we saw in class, global space also has variables for the function and module names. For example, when you define a function, it creates a folder for the function body and stores the name of that folder in a variable with the same name of the function.

We do not want you to draw these variables in global space. You can ignore what happens to function definitions (and module importing) for now.


Diagramming Conditionals

Be careful when executing code that contains conditionals (marked by if statements). Not every line of an if-else block of code will be executed. Consequently, not every line number will appear in the program counter box. Consider the following example:

Notice that Python executes the assignment statement on line 2, then the if statement on line 3. Because the boolean expression x==5 evaluates to False, Python skips all indented lines that follow line 3. Python executes the elif statement on line 5. Because the boolean expression x==4 evaluates to False, once again Python skips all the indented lines that follow line 5. Python executes line 7 (to discover that this time it need not evaluate any boolean expression at all!) and then executes each indented line after line 7--in this case, that is simply line 8. After line 8 is executed Python creates the None return value and the call frame is deleted.


Assignment Instructions

Remember that there are three memory locations that we talked about in class: global space, call frames, and heap space. Every time that you execute a line of code, these can all change. We want your drawings to reflect these changes while impressing upon you that the variables, program counters, and call frames are modified in place, not duplicated as each line of code executes.

For each problem, you will be given a function call. We want you to diagram the execution of four different function calls (one each in Questions 1, 2, 3, and 4). You will draw a single diagram that reflects the changes over time, by crossing out old values and inserting the new ones to the right. It is not enough to simply show the final state of the diagram. We want to see the intermediary steps. If a function call returns, we also want you to cross the frame off neatly so that its contents are still legible.

Some of the problems below will have associated values in both global space and heap space. For those problems, you should also diagram the evolution of both global space and heap space. Each line of code should trigger some change to your drawing.

Important: You must pay close attention to types in this assignment. The value 3 is an integer while the value 3.0 is a float. Hence they are different values. If you answer with an integer like 3 when the correct answer is a float like 3.0, we will deduct points.


Function isLongerThan

Consider the function isLongerThan, defined below:

1   def isLongerThan(s,n):
2       """Returns: True if string s is longer than len n.
3       Otherwise, return False.
4       
5       Precondition: s is a string, n is an int"""
6
7       length = len(s)
8       if (length > n):
9           return True
10      else:
11          print("Given string has too few characters.")
12          return False

Question 1.1: Diagram the Execution of a = isLongerThan("n",4)

Draw the execution of the statement a = isLongerThan("n",4). You will need to draw the evolution of the call frame over time. This will require several modifications to the same diagram. Begin by drawing the diagram when the frame is created. Now modify the diagram for each line of code that is executed (and only the lines which are executed). Since we are only drawing a single call frame, you must neatly indicate how it has changed over time.

When an assignment changes the value of an existing variable in the frame, legibly cross out the old value and write the new value in the box. If we cannot read the old value, we can give you points for it being correct.

In addition to the diagram of the call frame, you also need to diagram the evolution of the global variable a. The global space starts out empty (we are ignoring the variable for the function isLongerThan); at some point, the variable a will be created.

You do not need to draw the evolution of heap space, as there is nothing interesting in heap space for this problem. Also, you do not need to draw the result of print statements. Although it might be nice to show that (and the Python tutor does), we are diagramming the state of memory which is entirely distinct from what is printed to the screen by a program.

Question 1.2: Multiple Choice

Because our drawing convention does not have a good way of annotating the time of a variable's creation, we ask you to answer the following multiple choice question after you draw your diagram:

The variable a is created:
---------------------------
A) When Python executes line 1.
B) Just before Python calls isLongerThan.
C) Just after Python returns from isLongerThan.
D) The time of creation could change each time the code is executed.
E) There is not enough information given to answer the question.

Somewhere near your answer to Question 1.1, please write "The answer to Question 1.2 is F." But replace F with your answer.

Question 2: Diagram the Execution of b = isLongerThan(attempt,length)

For this question, suppose that you have the following (global) variables:

Repeat the previous exercise for the statement b = isLongerThan(attempt,length). Once again, you need to draw the evolution of global space as well as the individual frame diagram. The difference is that this time global space does not start out empty. In addition, the new global variable is now named b.


Functions on Student Objects

The next two problems have to do with the Student class. Objects of the Student class have three attributes, all of them strings:
  netid: the student's unique identifier (e.g., "abc123")
  am: course code for their morning course (e.g., “CS1110”)
  pm: course code for their afternoon course (e.g., “MATH1920”)
These objects are created by the constructor call Student("abc123","CS1110","MATH1920").

When dealing with objects, you should remember that a variable does not contain an object; it can only hold the identifier of the object. Therefore, the assignments

s1 = Student("abc123","HADM4300","VIEN1104")
s2 = Student("def456","PE1657","PE1130")
would produce the following contents in both global and heap space

folder-variables

In this case we have folders representing two Student objects, and two global variables storing these object identifiers. We made up the folder identifier numbers, as their value does not matter. If we ask you to make a Student object, you should feel free to make up the identifier number, but make sure it is different from the ones currently in s1 and s2.

Question 3: Diagram the Execution of course_swap(s1)

According to the definition of the Student class, a student takes only 2 classes: a morning class and an afternoon class. The function course_swap() allows students to swap their morning and afternoon courses. It takes a student as input and swaps the am and pm attributes; the netid attribute is left unaltered. We define this function as follows:

1  def course_swap(st):
2       """Swap the am and pm attributes of st
3
4       Example: The student (netid: "ee222", am: "CS1110", pm: "MATH1920") 
5       becomes (netid: "ee222", am: "MATH1920", pm: "CS1110")
6
7       Parameter st: the student to swap morning and afternoon courses
8       Precondition: st is (refers to, contains the ID of) a student object"""
9       tmp = st.am
10      st.am = st.pm
11      st.pm = tmp

The function course_swap is a procedure (i.e., it has no return statement) and so it does not need to be used in an assignment statement. Hence the call course_swap(s1) is a statement by itself.

Diagram the call course_swap(s1) given the global variables and heap space shown above. Your answer will in include a single diagram that is modified over time. Again, your diagram should show that the frame is created, the result of each line of code that is executed, and a then the frame is deleted.

Your diagram should also include the evolution of the global variables s1 and s2, and the corresponding objects in heap space. If the contents of any global variable change, or the contents of the object folders are altered, you should cross out the old value and write in the new value.

Question 4: Diagram the Execution of s1 = course_swap2(s2)

The function course_swap2(st) is similar to course_swap(st), except that it is a fruitful function instead of a procedure. We define the function as follows:

1  def course_swap2(st):
2      """Returns: A copy of st with the am and pm attributes swapped
3
4      Example: course_swap2 with argument ("ee222", "CS1110", "MATH1920")
5      creates the student object ("ee222",  "MATH1920", "CS1110")
6
7      Parameter st: the student whose schedule to swap
8      Precondition: st is (refers to, contains the ID of) a student object"""
9      newstu = Student("","","")
10     newstu.netid = st.netid
11     newstu.am = st.pm
12     newstu.pm = st.am
13     return newstu

Diagram the execution of the statement s1 = course_swap2(s2) given the global variables and heap space shown above. Assume that you are starting over with s1 and s2; do not include any changes from Question 3.

As always, your diagram should show that the frame is created, the results of each line of code being executed, and that the frame is deleted. You should show how the global space and heap space change over time. If the contents of any global variable change, or the contents of the object folders are altered, you should cross out the old value and write in the new value.


Working in Reverse

So far in this assignment, we have given you a function definition and asked you to draw the diagram for a function call. Now we want you to do the reverse. We give you the diagrams for several function calls and ask you to write the corresponding definition. All you have to do is write out the Python code. Any function definition is correct so long as it produces the frame shown for each call.

Question 5: Define the function alphabetize(word1,word2)

The function alphabetize has the following specification:

def alphabetize(word1,word2):
    """Returns: the two words alphabetized and concatenated.

    For Example:
    alphabetize('John', 'Wyatt') returns 'John Wyatt'
    alphabetize('Mary', 'Anne') returns 'Anne Mary'

    Parameter word1: first word
    Parameter word2: second word
    Precondition: word1 and word2 are strings"""

The following diagrams illustrate the function calls alphabetize('John', 'Wyatt') and alphabetize('Mary', 'Anne'), respectively.

dist

Complete the function definition. Remember: you must produce code whose execution would produce the above diagram. Completing the function according to spec is not enough!

Hint: The alphabetic ordering of two strings can be compared in a single line.


Finishing the Assignment

When you finish the assignment, put your name and netid at the top of the page. If you are working with a partner, then the partner's name must be on the assignment as well.


Creating a PDF for Submission

You will submit your work as a PDF. If you created your assignment on a computer, this should be relatively easy. Otherwise, you will need to scan your paper as a PDF. There are scanners in Olin and Uris Library to help you with this. You can also scan your homework using a mobile device. Please allow enough time do the scanning. Don't miss the deadline because of a scanning fiasco.

You may not submit multiple PDF files. If your scanner makes more than one file, you must combine them. On Windows, the program PDFtk can merge multiple PDF files into one file. In OS X, the built-in application Preview can be used to merge PDF files.


Upload your PDF to Gradescope

Please submit your assignment to Gradescope. An account has been created for you with your Cornell NetID email address. (You should have received an email from Gradescope earlier this week; check your spam folder if you cannot find it. If you still cannot find it, you can perform a password reset using your Cornell NetID email address.)

Once you've performed the upload, it's a good idea to view your online submission to make sure it is readable and complete.


Survey

We ask that you complete a feedback survey, which will be posted on Canvas on Feburary 16. We would like to know how long you spent on the assignment, your impression of the difficulty, and what could be done to improve it. Please complete the survey within a day of turning in the assignment. Remember that participation in surveys compromise 1% of your final grade. Your honest feedback is very helpful to us.


Authors: A. Bracy, T. Driskell, L. Kanina, A. Lee, L. Lee, S. Marschner, S. North, B. Posnick, H. Wang, W. White.