Instructions for listDiagrams

(produced at 21:35 UTC on 2024-10-23)

This task is part of project07 which is due at 23:00 EDT on 2024-10-29.

You have the option to work with a partner on this task if you wish. Working with a partner requires more work to coordinate schedules, but if you work together and make sure that you are both understanding the code you write, you will make progress faster and learn more.

You can download the starter code for this task using this link.

You can submit this task using this link.

Put all of your work for this task into the file listDiagrams.py
(which is provided among the starter files)

General Overview

This task involves defining six functions whose purpose is to create lists described by specific memory diagrams.

Let's begin with a sample memory diagram and showing several ways to define a function that creates it. Here's the diagram for a list we'll call list0: A diagram depicting the following memory listing: @0 is a list holding references to objects @1, @2, @1, and @2. Object @1 is a list holding the number 1 and a reference to object @2]. Object @2 is a list holding just the number 2.

We'll consider three different approaches for defining a zero-parameter function makeList0 that creates and returns the top list in the diagram.

Approach 1 for makeList0: Use variables to name shared sublists

In our makeList0 function, it's OK to define as many local variables as we want to help us, even if they don't appear in the diagram. We can use local variables to name shared sublists and then use these variables to achieve the sharing we desire.

We'll introduce variables shared2, shared1, and top, as shown in the following sequence of statements:

Statements Diagram so far
shared2 = [2] The variable 'shared2', which points to the value @2, which is a list holding the number 2.
shared1 = [1, shared2] The variable 'shared2' as before, along with a variable 'shared1', which points to the value @1, which is a list holding the number one in its first slot and an arrow referencing object @2 (i.e., the same object rerferenced by 'shared2') in its second slot.
top = [shared1, shared2, shared1, shared2] The variables 'shared1' and 'shared2' as before, with an additional variable 'top' which references an object @0 which is a list holding refernces to @1, @2, @1, and @2 in that order.

We can finish by encapsulating the steps for Approach 1 within a zero-parameter function makeList0:

def makeList0():
    """
    Returns the top list depicted in the memory diagram for list0.
    Uses Approach 1 to create the appropriate list structure.
    """
    shared2 = [2]                     
    shared1 = [1, shared2]            
    top = [shared1, shared2, shared1, shared2]
    return top

Approach 2 for makeList0: Use a single variable and assignment to list slots

A second approach for creating the list structure shown for list0 is to use a single variable (call it top) to create an initial list with four slots, where all slots but the last one contain bogus placeholder values that will be replaced in subsequent steps. Then we use assignment to list slots to create extra structure and achieve the desired result.

Statements Diagram so far
top = [3, 4, 5, [2]] A variable 'top' which references an object @0 that's a list, holding the numbers 3, 4, and 5 and then in the last slot a reference to a list @2 holding the number 2.
top[0] = [1, top[3]] The same 'top' variable, but now its first slot holds a reference to a list @1 which has two slots holding the number one and a reference to @2.
top[1] = top[3]
top[2] = top[0]
The same 'top' variable, but now the second and third slots have been replaced by references to @2 and @1 respectively.

We can finish by encapsulating the steps for Approach 2 within a zero-parameter function makeList0:

def makeList0():
    """
    Returns the top list depicted in the memory diagram for list0.
    Uses Approach 2 to create the appropriate list structure.
    """
    top = [3, 4, 5, [2]]
    top[0] = [1, top[3]]
    top[1] = top[3] 
    top[2] = top[0]
    return top

Approach 3 for makeList0: Use a single variable and .append

A third approach for creating the list structure shown for list0 is to use a single variable (call it top) and use only calls to the list .append method to create the desired diagram using the following steps:

Statements Diagram so far
top = [[1]] A variable 'top' referencing a list @0 with one slot, which references a list @1 with a single slot holding the number 1.
top[0].append([2]) The same variable, now with a second slot in @1 which references @2, a list containing a single slot that holds the number 2.
top.append(top[0][1]) The same variable, except now the object @0 (referenced by the variable 'top') has two slots, and the second references the object @2.
top.append(top[0]) The same variable with a third slot in @0 that references @1 (a second time, since the first slot of @0 also references @1).
top.append(top[1]) The same variable, now with four slots in @0, the last of which references @2.

We can finish by encapsulating the steps for Approach 3 within a zero-parameter function makeList0:

def makeList0():
    """
    Returns the top list depicted in the memory diagram for list0.
    Uses Approach 3 to create the appropriate list structure.
    """
    top = [[1]]
    top[0].append([2])
    top.append(top[0][1])
    top.append(top[0])
    top.append(top[1])
    return top

Your Task: Define Functions to Create the List Structures in Six Memory Diagrams

In the file listDiagrams.py, you will flesh out the definitions of six functions that create and return a list. Each function must have zero parameters and must return the top list for the structure depicted in the associated memory diagram.

In all of your functions, it is OK to use as many local variables as you want to name parts of the memory diagram, even though the memory digrams themselves do not show any variables. See the three versions of makeList0 above for examples of using local variables in this way.

In all of the functions, it is possible to use only a single local variable (like top in Approaches 2 and 3 for makeList0). If you want to give yourself an (ungraded) challenge, write your solutions using at most only local variable.

makeList1

The function makeList1 has zero parameters and returns a list with the following structure: An object @0 is a list with three slots, containing a reference to @1, the number 3, and a reference to @2. The object @1 is a list holding the numbers 1 and 2, while @2 is another, separate list holding the numbers 1 and 2.

makeList2

The function makeList2 has zero parameters and returns a list with the following structure: An object @0 which is a list holding two references to an object @1, with the number 3 between them. @1 is a list holding the numbers 1 and 2.

makeList3

The function makeList3 has zero parameters and returns a list with the following structure: An object @0 which is a 3-slot list, holding references to objects @1, @2, and @3. @1 is a list holding the number 1 followed by a reference to @2. @2 is a list holding a reference to @3 followed by the number 2. @3 is a list holding just the number 3.

makeList4

The function makeList4 has zero parameters and returns a list with the following structure: An object @0 which is a list with four slots, which hold references to objects @1, @2, @3, and @4. @1 is a list which holds the numbers 4 and 5. @2 is a list which holds refernces to @1 and @3 (in that order). @3 is a list which holds references to @1 and @4. @4 is a second list which holds the numbers 4 and 5, just like @1 did.

makeList5

The function makeList5 has zero parameters and returns a list with the following structure: An object @0 which is a list with two slots, holding a reference to the string 'parent', and a reference to an object @1. @1 is a list with two slots holding a reference to objects @2 and @3. @2 is a list with two slots holding a reference to the string 'child' and then a reference to object @0. @3 is a list with two slots holding a reference to the string 'child2' and a reference to @0.

Note that this diagram contains some string values; "parent", "child1", and "child2" are strings stored in list slots, not variable names (variables are shown without quotes, have an associated box, and do not have arrows pointing to them).

makeList6

(Defining this function is an extra goal.) The function makeList6 has zero parameters and returns a list with the following structure: An object @0 which is a list with 3 slots, holding a reference to an object @1, the number 6, and a second reference to @1. @1 is a list with 4 slots, holding the number 7, a reference to @0, the number 8, and a reference to @1 (itself).

Testing your Functions

You cannot tell just from the printed output of your functions whether they have the correct structure. For example, both list1 and list2 have the printed representation:

[[1, 2], 3, [1, 2]]

The list1 diagram from above, depicting an object @0 which is a list with 3 slots, containing a reference to object @1, the number 3, and a reference to object @2. Objects @1 and @2 are clones of each other, each is a list holding the numbers 1 and 2. The diagram for list 2 above, which shows an object @0 that is a list with three slots, holding a reference to object @1, the number 3, and a second reference to @1. @1 is a list holding the numbers 1 and 2.

The Python is operator (covered in Lec 11) is useful for determining which sublists are shared an which are not. For example, executing the statements

list1 = makeList1()
list2 = makeList2()
print('list1[0] is list1[2] =>', list1[0] is list1[2])
print('list2[0] is list2[2] =>', list2[0] is list2[2])

prints

list1[0] is list1[2] => False
list2[0] is list2[2] => True

By using suitable tests involving is (in conjunction with some other tests), it is possible to determine if the lists returned by the six makeListn functions have the correct structure.

As usual, we have supplied a test file test_listDiagrams.py which will use optimism to check that your results are correct, including key parts of their internal structure. We have also provided a file named show_listDiagrams.py which will print out a "memory report" for the result of each of your functions. This is kind of like a memory diagram in text format, and you can compare these to the memory reports shown in the examples below to see if things are correct.

In a memory report, each object gets assigned a label that starts with @, and instead of drawing arrows when one object references another, we write the label of the referenced object.

Examples

Examples for each function

Note that these examples show what the results look like as Python values, which is not enough to know what their memory diagrams look like, so each example also shows a memory report which shows how objects reference each other. In a memory report, each object is assigned a tag that starts with @, and when objects reference each other a tag is used where an arrow would be used in a memory diagram.

In []:
makeList1()
Out[]:
[[1, 2], 3, [1, 2]]
Memory Report
@0: [@1, 3, @2]
@1: [1, 2]
@2: [1, 2]
In []:
makeList2()
Out[]:
[[1, 2], 3, [1, 2]]
Memory Report
@0: [@1, 3, @1]
@1: [1, 2]
In []:
makeList3()
Out[]:
[[1, [[3], 2]], [[3], 2], [3]]
Memory Report
@0: [@1, @2, @3]
@1: [1, @2]
@2: [@3, 2]
@3: [3]
In []:
makeList4()
Out[]:
[[4, 5], [[4, 5], [[4, 5], [4, 5]]], [[4, 5], [4, 5]], [4, 5]]
Memory Report
@0: [@1, @2, @3, @4]
@1: [4, 5]
@2: [@1, @3]
@3: [@1, @4]
@4: [4, 5]
In []:
makeList5()
Out[]:
['parent', [['child1', [...]], ['child2', [...]]]]
Memory Report
@0: [@1, @2]
@1: 'parent'
@2: [@3, @5]
@3: [@4, @0]
@4: 'child1'
@5: [@6, @0]
@6: 'child2'
In []:
makeList6()
Out[]:
[[7, [...], 8, [...]], 6, [7, [...], 8, [...]]]
Memory Report
@0: [@1, 6, @1]
@1: [7, @0, 8, @1]

Rubric

Group goals:
 
unknown makeList1 result looks the same
The result of makeList1 must at least compare equal to the solution result, even if it has a different in-memory structure.
 
unknown The result of makeList1 must have the correct memory structure
The memory structure of the result produced by running makeList1 must match that of the result produced by the solution code, including which parts are aliases of each other.
 
unknown makeList2 result looks the same
The result of makeList2 must at least compare equal to the solution result, even if it has a different in-memory structure.
 
unknown The result of makeList2 must have the correct memory structure
The memory structure of the result produced by running makeList2 must match that of the result produced by the solution code, including which parts are aliases of each other.
 
unknown makeList3 result looks the same
The result of makeList3 must at least compare equal to the solution result, even if it has a different in-memory structure.
 
unknown The result of makeList3 must have the correct memory structure
The memory structure of the result produced by running makeList3 must match that of the result produced by the solution code, including which parts are aliases of each other.
 
unknown makeList4 result looks the same
The result of makeList4 must at least compare equal to the solution result, even if it has a different in-memory structure.
 
unknown The result of makeList4 must have the correct memory structure
The memory structure of the result produced by running makeList4 must match that of the result produced by the solution code, including which parts are aliases of each other.
 
unknown makeList5 result looks the same
The result of makeList5 must at least compare equal to the solution result, even if it has a different in-memory structure.
 
unknown The result of makeList5 must have the correct memory structure
The memory structure of the result produced by running makeList5 must match that of the result produced by the solution code, including which parts are aliases of each other.
 
unknown makeList6 must return the correct result
The result returned when your makeList6 function is run must match the solution result.
 
unknown The result of makeList6 must have the correct memory structure
The memory structure of the result produced by running makeList6 must match that of the result produced by the solution code, including which parts are aliases of each other.
 
unknown Define makeList1 with 0 parameters
Use def to define makeList1 with 0 parameters
 
unknown Use a return statement
Within the definition of makeList1 with 0 parameters, use return _ in at least one place.
 
unknown Define makeList2 with 0 parameters
Use def to define makeList2 with 0 parameters
 
unknown Use a return statement
Within the definition of makeList2 with 0 parameters, use return _ in at least one place.
 
unknown Define makeList3 with 0 parameters
Use def to define makeList3 with 0 parameters
 
unknown Use a return statement
Within the definition of makeList3 with 0 parameters, use return _ in at least one place.
 
unknown Define makeList4 with 0 parameters
Use def to define makeList4 with 0 parameters
 
unknown Use a return statement
Within the definition of makeList4 with 0 parameters, use return _ in at least one place.
 
unknown Define makeList5 with 0 parameters
Use def to define makeList5 with 0 parameters
 
unknown Use a return statement
Within the definition of makeList5 with 0 parameters, use return _ in at least one place.
 
unknown Define makeList6 with 0 parameters
Use def to define makeList6 with 0 parameters
 
unknown Use a return statement
Within the definition of makeList6 with 0 parameters, use return _ in at least one place.