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.
Table of Contents
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:
				 
				
When you execute the next line, the instruction counter becomes blank (because there is nowhere else to go), but you add the RETURN variable.
				 
				
				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
				
				
				 
				
				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.
				
				 
				
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.