@extends('template') @section('title') Lab 7: Part 1 - Drawing Memory Diagrams @stop @section('head') @stop @section('content') # Lab 7: Part 1 - Drawing Memory Diagrams In this part of the lab, instead of working on the computer, you will practice drawing and modifying memory diagrams on a whiteboard. In each part one of you will draw the diagram and then the other person will modify it. Make sure to check your answers using the links below to reveal what the diagram should look like. Feel free to take pictures of the whiteboard at each step if you want to retain them for your notes. Using a whiteboard is easier than using paper because you will frequently need to erase and re-draw arrows. ## Memory Diagram Reminders - Each variable gets a box. Small values, like numbers, booleans, or `None`, are written in a box. - Large values like strings or lists are written separately, with an arrow starting in the box that points to the value. * Each slot in a list is its own box that works the same way. - When you see an `=` sign, follow these steps: 1. Identify what is on the right-hand side: * If there's an expression on the right-hand side involving `+`, `*`, or another operator or function call, the result depends on the operation performed, and will usually be a newly-created object. * If the right-hand side just has a variable reference (possibly with indexing involved), then the result will be a copy of whatever is in the box that the right-hand expression identifies. 2. Identify which box is being updated based on the left-hand side. * If it's a variable, update that box. * If it involves indexing, figure out which box of which list is being updated. 3. Take the expression result and replace the contents of the box being updated with that value. Do not create any new arrows at this point unless the result you are using is an arrow (in that case, the new arrow points to the same place as the one you're copying). - Besides assignment using `=`, Specific methods like `append`, `insert`, and `pop` can change the contents of the object they apply to. `+=` and related updating-assignments also change the value on their left-hand side. - **Arrows cannot point to slots within lists.** They can only point to strings or entire lists. ## Simple variable diagrams ### 1. Basic Values
Partner A
On a whiteboard, draw a memory diagram for the state at the end of this code: ```py num = 10 word = 'hello' ```
Check your answer ![A memory diagram corresponding to the following memory report: um = 10; word = @1; @1: 'hello'](basicVars1.svg){.md}
Partner B
Modify your partner's diagram to update it based on the following additional assignments: ```py word = 10 value = num * word ```
Check your answer ![A memory diagram corresponding to the following memory report: num = 10; word = 10; value = 100](basicVars2.svg){.md}
### 2. Duplicates
Partner B
Draw the memory diagram for the the end of this code: ```py a = 10 b = 10 c = 'hello' d = 'hello' ```
Check your answer ![A memory diagram corresponding to the following memory report: a = 10; b = 10; c = @1; d = @2; @1: 'hello'; @2: 'hello' Note: It would also be valid to use @1 for both c and d.](duplicates1.svg){.md} Note that it would also be valid to draw a second arrow in box 'd' that points to the same 'hello' you drew for 'c'. Becuase strings are **immutable**, it doesn't matter to the programmer whether they are aliases or clones, and Python often creates aliases instead of clones when it can.
Partner A
Modify your partner's diagram to update it based on the following additional assignments: ```py b = d c = a ```
Check your answer ![A memory diagram corresponding to the following memory report: a = 10; b = @2; c = 10; d = @2; @2: 'hello'](duplicates2.svg){.md} Note that it's also valid to draw a second arrow and 'hello' for 'b', but that's more work than just drawing an arrow to the 'hello' you already drew.
### 3. Swap
Partner A
Draw the memory diagram for the the end of this code: ```py x = 'A' y = x + 'B' ```
Check your answer ![A memory diagram corresponding to the following memory report: x = @1; y = @2; @1: 'A'; @2: 'AB'](swap1.svg){.md} Note that it's not valid to draw any arrows between x and y or between their values.
Partner B
Now modify that based on these lines of code *(Reminder: code executes one line at a time, using whatever variable values have been defined up to that point)*: ```py x = y y = x ```
Check your answer ![A memory diagram corresponding to the following memory report: x = @2; y = @2; @2: 'AB'](swap2.svg){.md} Note that when we assign 'x = y', the old value of x is thrown away, since no other variables reference it.
Partner A
Now modify the diagram based on these addiional lines of code: ```py x += 'C' tmp = x x = y y = tmp ```
Check your answer ![A memory diagram corresponding to the following memory report: x = @1; y = @2; tmp = @1; @1: 'ABC'; @2: 'AB'](swap3.svg){.md} Here we used a third variable to help facilitate swapping x and y. Note that you don't have to draw the arrows crossed like this, but if you update things one-by-one, your arrows may end up this way unless you re-draw the strings 'ABC' and 'AB'. Putting 'ABC' below 'AB' avoids having to cross any arrows. The final value of `x` here is `'AB'` and the final value of `y` (and also of `tmp`) is `'ABC'`.
## Diagrams for Lists ### 4. List Basics
Partner B
Draw a memory diagram for the end of these lines of code: ```py x = [11, 22] y = x z = list(x) # creates a clone ```
Check your answer ![A memory diagram corresponding to the following memory report: x = @1; y = @1; z = @2; @1: [11, 22]; @2: [11, 22]](lists1.svg){.md}
Partner A
Now modify that diagram based on the following additional code that uses indexing and slicing: ```py x0 = x[0] y1 = y[1] zs = z[0:1] ```
Check your answer ![A memory diagram corresponding to the following memory report: x = @1; y = @1; z = @2; @1: [11, 22]; @2: [11, 22]; x0 = 11; y1 = 22; zs = @3; @3: [11]](lists2.svg){.md} Note that slicing creates a new list, and that indexing does NOT create new references when the items at the indices used are numbers. (If the thing in a slot being indexed slot is a reference, you'll get a copy of that reference).
Partner B
Now add this variable to your diagram: ```py all = [x, y, z] ```
Check your answer ![A memory diagram corresponding to the following memory report: x = @1; y = @1; z = @2; @1: [11, 22]; @2: [11, 22]; x0 = 11; y1 = 22; zs = @3; all = @4; @3: [11]; @4: [@1, @1, @2]](lists3.svg){.md}
### 5. List Mutation
Partner A
Draw a memory diagram for the end of these lines of code: ```py nums = [7, 23, 51] alias = nums clone = nums[:] # slice from start to end ```
Check your answer ![A memory diagram corresponding to the following memory report: nums = @1; alias = @1; clone = @2; @1: [7, 23, 51]; @2: [7, 23, 51]](mutability1.svg){.md}
Partner B
Now modify that diagram based on the following additional code: ```py ext = clone + [96] alias.append(121) ```
Check your answer ![A memory diagram corresponding to the following memory report: nums = @1; alias = @1; clone = @2; ext = @3; @1: [7, 23, 51, 121]; @2: [7, 23, 51]; @3: [7, 23, 51, 96]](mutability2.svg){.md}
Partner A
Next draw the diagram after these additional lines: ```py p = nums.pop(1) clone.insert(0, 3) nl = len(nums) al = len(alias) cl = len(clone) el = len(ext) ```
Check your answer ![A memory diagram corresponding to the following memory report: nums = @1; alias = @1; clone = @2; ext = @3; @1: [7, 51, 121]; @2: [3, 7, 23, 51]; @3: [7, 23, 51, 96]; p = 23; nl = 3; al = 3; cl = 4; el = 4](mutability3.svg){.md}
Partner B
Finally modify the diagram to account for this line *(note: part of this line of code does NOT make sense)*: ```py alias = alias.append(239) ```
Check your answer ![A memory diagram corresponding to the following memory report: nums = @1; alias = None; clone = @2; ext = @3; @1: [7, 23, 51, 121, 239]; @2: [7, 23, 51]; @3: [7, 23, 51, 96]](mutability4.svg){.md} Note that `append` modifies the list you apply it to, but its result value is `None`. You should *never* write `variable = variable.append(...)` because this will replace the variable's value with `None`, which isn't useful. Just write `variable.append(...)` on its own instead, as we did in the second step for this example. Note also that the number does in fact get appended to the list in question, which we can still see because `alias` was an alias of `nums`. Also note that the length of the nums list is now 4, and `alias` isn't a list any more, but the `nl` and `al` variables don't get updated: they hold the old length values computed previously.
### 6. List Mutation 2
Partner A
Write the memory diagram for the end of this code: ```py x = [80, 75, 50] y = x z = [x, y[1]] ```
Check your answer ![A memory diagram corresponding to the following memory report: x = @1; y = @1; z = @2; @1: [80, 75, 50]; @2: [@1, 75]](xyz1.svg){.md}
Partner B
Now modify that to reflect the state after these two lines have been run: ```py x[0] = 100 y.append('hi') ```
Check your answer ![A memory diagram corresponding to the following memory report: x = @1; y = @1; z = @2; @1: [100, 75, 50, @3]; @2: [@1, 75]; @3: 'hi'](xyz2.svg){.md}
Partner A
Finally show what memory looks like after these two additional lines: ```py z.insert(1, x[1] + y[1]) removed = z[0].pop(1) ```
Check your answer ![A memory diagram corresponding to the following memory report: x = @1; y = @1; z = @2; removed = 75; @1: [100, 50, @3]; @2: [@1, 150, 75]; @3: 'hi'](xyz3.svg){.md}
### 7. Mutation in a Loop
Partner B
Draw the memory diagram for the end of this code: ```py s = [] t = [] for i in range(3, 25, 7): s.append(i) ```
Check your answer ![A memory diagram corresponding to the following memory report: s = @1; t = @2; i = 17; @1: [3, 10, 17, 24]; @2: []](loopy1.svg){.md} Note that since `t` remains empty, it ends up as an empty box without any slots in it.
Partner A
Now draw the memory diagram after these additional lines of code have run: ```py for i in range(3): t.append(s[i]) ```
Check your answer ![A memory diagram corresponding to the following memory report: s = @1; t = @2; i = 2; @1: [3, 10, 17, 24]; @2: [3, 10, 17]](loopy2.svg){.md} This code copies the first three entries of `s` into `t`. Note how re-using `i` as the loop variable changes its value. Although not shown here, `i` changes value in every iteration of the loop.
Partner B
Next, draw the memory diagram after these additional lines of code have run *(note: these lines of code don't make a lot of sense)*: ```py for x in s: x += 1 ```
Check your answer ![A memory diagram corresponding to the following memory report: s = @1; t = @2; i = 2; x = 25; @1: [3, 10, 17, 24]; @2: [3, 10, 17]](loopy3.svg){.md} Note that values in `s` are unchanged: the loop copies those values into `x`, and then changes `x`. This code doesn't make sense because `x` gets overwritten in every iteration, and nothing else is changed. We could have just said `x = s[-1] + 1` to set `x` equal to one more than the last element in `s`.
Partner A
Next, draw the memory diagram after these additional lines of code have run *(note: these lines of code make more sense than the ones above)*: ```py for i in range(len(s)): s[i] += 1 ```
Check your answer ![A memory diagram corresponding to the following memory report: s = @1; t = @2; i = 3; x = 25; @1: [4, 11, 18, 25]; @2: [3, 10, 17]](loopy4.svg){.md} Here we actually modify the values in `s` within the loop, adding 1 to each value. That's because we're using indexing directly to set values within the loop. Note that `i` also changes since we used it as our loop variable.
Partner B
Finally, draw the memory diagram after these lines: ```py u = [] for x in t: u.insert(0, x) ```
Check your answer ![A memory diagram corresponding to the following memory report: s = @1; t = @2; i = 3; x = 17; u = @3; @1: [4, 11, 18, 25]; @2: [3, 10, 17]; @3: [17, 10, 3]](loopy5.svg){.md} This code copies `t` in reverse order, since each new item it sees is farther along in `t` but is being added to the beginning of `u`. Note that `x` also gets overwritten.
Whew! Done with drawing memory diagrams. Now move onto the next part of the lab where you'll get practice writing code involving memory diagrams. @include('/labs/lab07/_toc') @stop