Pick someone in your group to join Discord.
+It's fine if multiple people join, but one is enough.
+
+
Now switch to Pensieve:
+
+
+
Everyone: Go to discuss.pensieve.co and log in with your @berkeley.edu email, then enter your group number. (Your group number is the number of your Discord channel.)
+
+
+
Once you're on Pensieve, you don't need to return to this page; Pensieve has all the same content (but more features). If for some reason Penseive doesn't work, return to this page and continue with the discussion.
+
+
Post in the #help channel on Discord if you have trouble.
+
+
+
Getting Started
+
+
+
Everyone go around and say your name, just in case someone forgot.
+
+
For fun: Think of a big word with at least three syllables, such as
+"solitary" or "conundrum" or "ominous". Try to use it as many times as you can
+during today's discussion, but in ways that don't give away that it's your big
+word. At the end, your group will try to guess each person's big word. Whoever
+uses their big word the most times (and at least twice) without their group
+guessing it wins. (You win nothing; it's just a game.)
+
+
To get help from a TA, send a message to the discuss-queue channel with
+the @discuss tag and your discussion group number.
+
+
If you have fewer than 4 people in your group, you can merge with another group in the room with you.
+
+
The most common suggestion from last discussion was to add some hints, so we have.
+The second most common suggestion was to encourage more discussion and collaboration.
+Discuss and collaborate!
+
+
+
Trees
+
+
+
For a tree t:
+
+
+
Its root label can be any value, and label(t) returns it.
+
Its branches are trees, and branches(t) returns a list of branches.
+
An identical tree can be constructed with tree(label(t), branches(t)).
+
You can call functions that take trees as arguments, such as is_leaf(t).
+
That's how you work with trees. No t == x or t[0] or x in t or list(t), etc.
+
There's no way to change a tree (that doesn't violate an abstraction barrier).
+
+
+
Here's an example tree t1, for which its branch branches(t1)[1] is t2.
A path is a sequence of trees in which each is the parent of the next.
+
+
+
+
+
+
You don't need to know how tree, label, and branches are implemented in
+order to use them correctly, but here is the implementation from lecture.
+
+
def tree(label, branches=[]):
+ for branch in branches:
+ assert is_tree(branch), 'branches must be trees'
+ return [label] + list(branches)
+
+def label(tree):
+ return tree[0]
+
+def branches(tree):
+ return tree[1:]
+
+def is_leaf(tree):
+ return not branches(tree)
+
+def is_tree(tree):
+ if type(tree) != list or len(tree) < 1:
+ return False
+ for branch in branches(tree):
+ if not is_tree(branch):
+ return False
+ return True
+
+
+
+
+
+
Q1: Warm Up
+
+
+
What value is bound to result?
+
+
result = label(min(branches(max([t1, t2], key=label)), key=label))
+
+
+
+
+
+
Solution
+
+
6: max([t1, t2], key=label) evaluates to the t2 tree because its label 5 is
+larger than t1's label 3. Among t2's branches (which are leaves), the left one
+labeled 6 has a smaller label.
+
+
+
+
How convoluted! (That's a big word.)
+
+
+
+
+
+
Here's a quick refresher on how key functions work with max and min,
+
+
max(s, key=f) returns the item x in s for which f(x) is largest.
+
+
>>> s = [-3, -5, -4, -1, -2]
+>>> max(s)
+-1
+>>> max(s, key=abs)
+-5
+>>> max([abs(x) for x in s])
+5
+
+
+
+
Therefore, max([t1, t2], key=label) returns the tree with the largest label,
+in this case t2.
+
+
+
In case you're wondering, this expression does not violate an abstraction
+barrier. [t1, t2] and branches(t) are both lists (not trees), and so it's
+fine to call min and max on them.
+
+
+
+
+
Q2: Has Path
+
+
+
Implement has_path, which takes a tree t and a list p. It returns whether
+there is a path from the root of t with labels p. For example, t1 has a
+path from its root with labels [3, 5, 6] but not [3, 4, 6] or [5, 6].
+
+
Important: Before trying to implement this function, discuss these questions
+from lecture about the recursive call of a tree processing function:
+
+
+
What recursive calls will you make?
+
What type of values do they return?
+
What do the possible return values mean?
+
How can you use those return values to complete your implementation?
+
+
+
If you get stuck, you can view our answers to these questions by clicking the
+hint button below, but please don't do that until your whole group agrees.
+
+
+
+
+What recursive calls will you make?
+
+
As you usual, you will call has_path on each branch b. You'll make this
+call after comparing p[0] to label(t), and so the second argument to
+has_path will be the rest of p: has_path(b, p[1:]).
+
+
What type of values do they return?
+
+
has_path always returns a bool value: True or False.
+
+
What do the possible return values mean?
+
+
If has_path(b, p[1:]) returns True, then there is a path through branch b
+for which p[1:] are the node labels.
+
+
How can you use those return values to complete your implementation?
+
+
If you have already checked that label(t) is equal to p[0], then a True
+return value means there is a path through t with labels p using that branch
+b. A False value means there is no path through that branch, but there
+might be path through a different branch.
+
def has_path(t, p):
+ """Return whether tree t has a path from the root with labels p.
+
+ >>> t2 = tree(5, [tree(6), tree(7)])
+ >>> t1 = tree(3, [tree(4), t2])
+ >>> has_path(t1, [5, 6]) # This path is not from the root of t1
+ False
+ >>> has_path(t2, [5, 6]) # This path is from the root of t2
+ True
+ >>> has_path(t1, [3, 5]) # This path does not go to a leaf, but that's ok
+ True
+ >>> has_path(t1, [3, 5, 6]) # This path goes to a leaf
+ True
+ >>> has_path(t1, [3, 4, 5, 6]) # There is no path with these labels
+ False
+ """
+ if p == [label(t)]:
+ return True
+ elif label(t) != p[0]:
+ return False
+ else:
+ return any([has_path(b, p[1:]) for b in branches(t)])
+
+
+
+
+
+
+
+
The base case expression p == [label(t)] checks two things: that p has one item and that the item is equal to label(t). Longer expressions (that don't fit the template) would also work, such as if len(p) == 1 and p[0] == label(t).
+
+
The recursive case expresses that if a path through some branch b is labeled
+p[1:], then there is a path through t labeled p.
+
+
+
+
If your group needs some guidance, you can click on the hints below, but please
+talk with your group first before reading the hints.
+
+
+
+
+The first base case should check whether p is a list of length one with the
+label of t as its only element. The second base case should check if the
+first element of p matches the label of t.
+
+
+
+
+
+When entering the recursive case, your code should already have checked that
+p[0] is equal to label(t), and so all that's left to check is that p[1:]
+contains the labels in a path through one of the branches. One way is with
+this template:
+
+
for ____:
+ if ____:
+ return True
+return False
+
+
+
+
+
Discussion Time! Can the else case of has_path be written in just one
+line? Why or why not? You can ignore how fast the function will run.
+When your group has an answer, send a message to the discuss-queue channel with
+the @discuss tag, your discussion group number, and the message "Maybe?" and a
+member of the course staff will join your voice channel to hear your answer and
+give feedback.
+
+
+
+
+
Q3: Find Path
+
+
+
Implement find_path, which takes a tree t with unique labels and a value
+x. It returns a list containing the labels of the nodes along a path from the
+root of t to a node labeled x.
+
+
If x is not a label in t, return None. Assume that the labels of t are unique.
+
+
First talk through how to make and use the recursive call.
+(Try it yourselves; don't just click the hint button. That's how you learn.)
+
+
+
+
+What recursive calls will you make?
+
+
find_path(b, x) on each branch b.
+
+
What type of values do they return?
+
+
Each recursive call will either return None or a non-empty list of node labels.
+
+
What do the possible return values mean?
+
+
If find_path(b, x) returns None, then x does not appear in b. If
+find_path(b, x) returns a list, then it contains the node labels for a path
+through b that ends with the node labeled x.
+
+
How can you use those return values to complete your implementation?
+
+
If a list is returned, then it contains all of the labels in the path except
+label(t), which must be placed at the front.
+
+The base case return value [label(t)] creates a one-element list of the labels along a path that starts at the root of t and also ends there, since the root is labeled x.
+
+
The assignment path = find_path(b, x) allows the return value of this recursive call to be used twice: once to check if it's None (which is a false value) and again to build a longer list.
+
+
The expression [label(t)] + path for a tree t and list path creates a longer list that starts with the label of t and continues with the elements of path.
+
+
+
+
Please don't view the hints until you've discussed with your group and can't make progress.
+
+
+
+
+If x is the label of t, then return a list with one element that contains the label of t.
+
+
+
+
+
+
Assign path to the result of a
+recursive call to find_path(b, x) so that you can both check whether it's
+None and extend it if it's a list.
+
+
For a list path and a value v, the expression [v] + path creates a longer
+list that starts with v and then has the elements of path.
+
+
+
Description Time! When your group has completed this question, it's time to
+describe why this function does not have a base case that uses is_leaf. Come
+up with an explanation as a group, pick someone to present your answer, and then
+send a message to the discuss-queue channel with the @discuss tag, your
+discussion group number, and the message "Found it!" and a member of the course
+staff will join your voice channel to hear your description and give feedback.
+
+
+
+
+
Document the Occasion
+
+
+
For each person, the rest of the group should try to guess their big word
+(from the Getting Started section). The group only gets one guess. After they
+guess, reveal your big word and how many times you used it during discussion.
+
+
Please all fill out the attendance form (one submission per person per week).
Pick someone in your group to join Discord.
+It's fine if multiple people join, but one is enough.
+
+
Now switch to Pensieve:
+
+
+
Everyone: Go to discuss.pensieve.co and log in with your @berkeley.edu email, then enter your group number. (Your group number is the number of your Discord channel.)
+
+
+
Once you're on Pensieve, you don't need to return to this page; Pensieve has all the same content (but more features). If for some reason Penseive doesn't work, return to this page and continue with the discussion.
+
+
Post in the #help channel on Discord if you have trouble.
+
+
+
Getting Started
+
+
+
Say your name and share a favorite place on the Berkeley campus or surrounding
+city that you've discovered. Try to pick a place that others might not have been
+yet. (But if the room you're in now is your favorite place on campus, that's ok
+too.)
+
+
+
+
+McCone Hall has a nice view from the 5th floor balcony.
+
+
+
+
Generators
+
+
+
A generator is an iterator that is returned by calling a generator function, which is a function that contains yield statements instead of return statements. The ways to use an iterator are to call next on it or to use it as an iterable (for example, in a for statement).
+
+
+
Q1: Big Fib
+
+
+
This generator function yields all of the Fibonacci numbers.
+
+
def gen_fib():
+ n, add = 0, 1
+ while True:
+ yield n
+ n, add = n + add, n
+
+
+
+
Explain the following expression to each other so that everyone
+understands how it works. (It creates a list of the first 10 Fibonacci numbers.)
+
+
(lambda t: [next(t) for _ in range(10)])(gen_fib())
+
+
+
+
Then, complete the expression below by writing only names and parentheses in the
+blanks so that it evaluates to the smallest Fibonacci number that is larger than
+2024.
+
+
Talk with each other about what built-in functions might be helpful, such as map, filter, list, any, all, etc. (Click on these function names to view their documentation.) Try to
+figure out the answer without using Python. Only run the code when your group
+agrees that the answer is right. This is not the time for guess-and-check.
def gen_fib():
+ n, add = 0, 1
+ while True:
+ yield n
+ n, add = n + add, n
+
+next(filter(lambda n: n > 2024, gen_fib()))
+
+
+
+
+
+
+
+
+
+
One solution has the form: next(____(lambda n: n > 2024, ____)) where the first
+blank uses a built-in function to create an iterator over just large numbers and the second blank creates
+an iterator over all Fibonacci numbers.
+
+
+
+
+
+
+
+
Surprise! There's no hint here. If you're still stuck, it's time to get help
+from the course staff.
+
+
+
+
+
+
+
Q2: Something Different
+
+
+
Implement differences, a generator function that takes t, a non-empty
+iterator over numbers. It yields the differences between each pair of adjacent
+values from t. If t iterates over a positive finite number of values n,
+then differences should yield n-1 times.
def differences(t):
+ """Yield the differences between adjacent values from iterator t.
+
+ >>> list(differences(iter([5, 2, -100, 103])))
+ [-3, -102, 203]
+ >>> next(differences(iter([39, 100])))
+ 61
+ """
+ last_x = next(t)
+ for x in t:
+ yield x - last_x
+ last_x = x
+
+
+
+
+
+
+
+
Presentation Time. Work together to explain why differences will always
+yield n-1 times for an iterator t over n values. Pick someone who didn't
+present to the course staff last week to present your group's answer, and then
+send a message to the discuss-queue channel with the @discuss tag, your
+discussion group number, and the message "We beg to differ!" and a member of the
+course staff will join your voice channel to hear your description and give
+feedback.
+
+
+
+
+
Intermission
+
+
+
We're lazy (like an iterator) and used ChatGPT to generate a generator joke...
+
+
+
+
+
+
Because it was skilled at knowing when to "return" to the recipe and when to "yield" to improvisation!
+
+
+
+
+
Q3: Partitions
+
+
+
Tree-recursive generator functions have a similar structure to regular
+tree-recursive functions. They are useful for iterating over all possibilities.
+Instead of building a list of results and returning it, just yield each
+result.
+
+
You'll need to identify a recursive decomposition: how to express the answer
+in terms of recursive calls that are simpler. Ask yourself what will be yielded
+by a recursive call, then how to use those results.
+
+
Definition. For positive integers n and m, a partition of n using
+parts up to size m is an addition expression of positive integers up to m in
+non-decreasing order that sums to n.
+
+
Implement partition_gen, a generator functon that takes positive n and m.
+It yields the partitions of n using parts up to size m as strings.
+
+
Reminder: For the partitions function we studied in lecture
+(video), the recursive decomposition was to
+enumerate all ways of partitioning n using at least one m and then to
+enumerate all ways with no m (only m-1 and lower).
def partition_gen(n, m):
+ """Yield the partitions of n using parts up to size m.
+
+ >>> for partition in sorted(partition_gen(6, 4)):
+ ... print(partition)
+ 1 + 1 + 1 + 1 + 1 + 1
+ 1 + 1 + 1 + 1 + 2
+ 1 + 1 + 1 + 3
+ 1 + 1 + 2 + 2
+ 1 + 1 + 4
+ 1 + 2 + 3
+ 2 + 2 + 2
+ 2 + 4
+ 3 + 3
+ """
+ assert n > 0 and m > 0
+ if n == m:
+ yield str(n)
+ if n - m > 0:
+ for p in partition_gen(n - m, m):
+ yield p + ' + ' + str(m)
+ if m > 1:
+ yield from partition_gen(n, m-1)
+
+
+
+
+
+
+
+
+Yield a partition with just one element, n. Make sure you yield a string.
+
+
+
+
+
+The first recursive case uses at least one m, and so you will need to yield a string that starts with p but also includes m. The second recursive case only uses parts up to size m-1. (You can implement the second case in one line using yield from.)
+
+
+
Presentation Time. If you have time, work together to explain
+why this implementation of partition_gen does not include base cases for
+n < 0, n == 0, or m == 0 even though the original implementation of
+partitions from lecture
+(video) had all three.
+Pick someone who didn't present to the course staff this week or last week to
+present your group's answer, and then send a message to the discuss-queue
+channel with the @discuss tag, your discussion group number, and the message
+"We're positive!" and a member of the course staff will join your voice channel
+to hear your description and give feedback.
+
+
+
Document the Occasion
+
+
+
Please all fill out the attendance form (one submission per person per week).
Pick someone in your group to join Discord.
+It's fine if multiple people join, but one is enough.
+
+
Now switch to Pensieve:
+
+
+
Everyone: Go to discuss.pensieve.co and log in with your @berkeley.edu email, then enter your group number. (Your group number is the number of your Discord channel.)
+
+
+
Once you're on Pensieve, you don't need to return to this page; Pensieve has all the same content (but more features). If for some reason Penseive doesn't work, return to this page and continue with the discussion.
+
+
Post in the #help channel on Discord if you have trouble.
+
+
+
Getting Started
+
+
+
Say your name, another class you're taking besides CS 61A, and something you've
+practiced for a while, such as playing an instrument, juggling, or martial arts.
+Did you discover any common interests among your group members?
+
+
+
Q1: Draw
+
+
+
The draw function takes a list hand and a list of unique non-negative
+integers positions that are all less than the length of hand. It removes
+hand[p] for each p in positions and returns a list of those elements in
+the order they appeared in hand (not the order they appeared in positions).
+
+
Fill in each blank with one of these names: list, map, filter, reverse,
+reversed, sort, sorted, append, insert, index, remove, pop,
+zip, or sum. See the built-in functions and
+list methods
+documentation for descriptions of what these do.
+
+
Discussion Time: Before writing anything, talk as a group about what
+process you'll implement in order to make sure the right cards are removed and
+returned. Try not to guess-and-check! The purpose of discussion is for you to
+try to solve problems without the help of an interpreter checking your work.
def draw(hand, positions):
+ """Remove and return the items at positions from hand.
+
+ >>> hand = ['A', 'K', 'Q', 'J', 10, 9]
+ >>> draw(hand, [2, 1, 4])
+ ['K', 'Q', 10]
+ >>> hand
+ ['A', 'J', 9]
+ """
+ return list(reversed( [hand.pop(i) for i in reversed(sorted(positions))] ))
+
+
+
+
+
+
+
+
+For a list s and integer i, s.pop(i) returns and removes the ith
+element, which changes the position (index) of all the later elements but
+does not affect the position of prior elements.
+
+
+
+
+
+Calling reversed(s) on a list s returns an iterator. Calling
+list(reversed(s)) returns a list of the elements in s in reversed order.
+
+
+
Aced it? Give yourselves a hand!
+
+
+
Object-Oriented Programming
+
+
+
A productive approach to defining new classes is to determine what instance
+attributes each object should have and what class attributes each class should
+have. First, describe the type of each attribute and how it will be used, then
+try to implement the class's methods in terms of those attributes.
+
+
+
Q2: Keyboard
+
+
+
Overview: A keyboard has a button for every letter of the alphabet. When a
+button is pressed, it outputs its letter by calling an output function (such
+as print). Whether that letter is uppercase or lowercase depends on how many
+times the caps lock key has been pressed.
+
+
First, implement the Button class, which takes a lowercase letter (a
+string) and a one-argument output function, such as Button('c', print).
+
+
The press method of a Button calls its output attribute (a function) on
+its letter attribute: either uppercase if caps_lock has been pressed an odd
+number of times or lowercase otherwise. The press method also increments
+pressed and returns the key that was pressed. Hint: 'hi'.upper() evaluates
+to 'HI'.
+
+
Second, implement the Keyboard class. A Keyboard has a dictionary called
+keys containing a Button (with its letter as its key) for each letter in
+LOWERCASE_LETTERS. It also has a list of the letters typed, which may be
+a mix of uppercase and lowercase letters.
+
+
The type method takes a string word containing only lowercase letters. It
+invokes the press method of the Button in keys for each letter in word,
+which adds a letter (either lowercase or uppercase depending on caps_lock) to
+the Keyboard's typed list. Important: Do not use upper or letter in
+your implementation of type; just call press instead.
+
+
Read the doctests and talk about:
+
+
+
Why it's possible to press a button repeatedly with .press().press().press().
+
Why pressing a button repeatedly sometimes prints on only one line and sometimes
+ prints multiple lines.
+
Why bored.typed has 10 elements at the end.
+
+
+
Discussion Time: Before anyone types anything, have a conversation
+describing the type of each attribute and how it will be used. Start with
+Button: how will letter and output be used? Then discuss Keyboard: how
+will typed and keys be used? How will new letters be added to the list
+called typed each time a Button in keys is pressed? Call the staff if
+you're not sure! Once everyone understands the answers to these questions, you
+can try writing the code together.
LOWERCASE_LETTERS = 'abcdefghijklmnopqrstuvwxyz'
+
+class CapsLock:
+ def __init__(self):
+ self.pressed = 0
+
+ def press(self):
+ self.pressed += 1
+
+class Button:
+ """A button on a keyboard.
+
+ >>> f = lambda c: print(c, end='') # The end='' argument avoids going to a new line
+ >>> k, e, y = Button('k', f), Button('e', f), Button('y', f)
+ >>> s = e.press().press().press()
+ eee
+ >>> caps = Button.caps_lock
+ >>> t = [x.press() for x in [k, e, y, caps, e, e, k, caps, e, y, e, caps, y, e, e]]
+ keyEEKeyeYEE
+ >>> u = Button('a', print).press().press().press()
+ A
+ A
+ A
+ """
+ caps_lock = CapsLock()
+
+ def __init__(self, letter, output):
+ assert letter in LOWERCASE_LETTERS
+ self.letter = letter
+ self.output = output
+ self.pressed = 0
+
+ def press(self):
+ """Call output on letter (maybe uppercased), then return the button that was pressed."""
+ self.pressed += 1
+ if self.caps_lock.pressed % 2 == 1:
+ self.output(self.letter.upper())
+ else:
+ self.output(self.letter)
+ return self
+
+
+
+
+
+
+
+
+Since self.letter is always lowercase, use self.letter.upper() to produce the uppercase version.
+
class Keyboard:
+ """A keyboard.
+
+ >>> Button.caps_lock.pressed = 0 # Reset the caps_lock key
+ >>> bored = Keyboard()
+ >>> bored.type('hello')
+ >>> bored.typed
+ ['h', 'e', 'l', 'l', 'o']
+ >>> bored.keys['l'].pressed
+ 2
+
+ >>> Button.caps_lock.press()
+ >>> bored.type('hello')
+ >>> bored.typed
+ ['h', 'e', 'l', 'l', 'o', 'H', 'E', 'L', 'L', 'O']
+ >>> bored.keys['l'].pressed
+ 4
+ """
+ def __init__(self):
+ self.typed = []
+ self.keys = {c: Button(c, self.typed.append) for c in LOWERCASE_LETTERS}
+
+ def type(self, word):
+ """Press the button for each letter in word."""
+ assert all([w in LOWERCASE_LETTERS for w in word]), 'word must be all lowercase'
+ for w in word:
+ self.keys[w].press()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Presentation Time: Describe how new letters are added to
+typed each time a Button in keys is pressed. Instead of just reading
+your code, say what it does (e.g., "When the button of a keyboard is pressed ...").
+One short sentence is enough to describe how new letters are added to typed.
+When you're ready, send a message to the #discuss-queue channel with the
+@discuss tag, your discussion group number, and the message "Put it on our
+tab!" and a member of the course staff will join your voice channel to hear your
+description and give feedback.
+
+
+
Q3: Bear
+
+
+
Implement the SleepyBear, and WinkingBear classes so that calling their print method
+matches the doctests. Use as little code as possible and try not to
+repeat any logic from Eye or Bear. Each blank can be filled with just two
+short lines.
+
+
Discussion Time: Before writing code, talk about what is different about a
+SleepyBear and a Bear. When using inheritance, you only need to implement
+the differences between the base class and subclass. Then, talk about what is
+different about a WinkingBear and a Bear. Can you think of a way to make
+the bear wink without a new implementation of print?
NEW: From now on, we'll still use Pensieve, but we've removed the voice chat from Pensieve. Use Discord for voice chat with the course staff. It's more reliable and includes screensharing. Write to @discuss in the #discuss-queue channel on Discord at any time, and a member of the course staff will join your voice channel.
+
+
Pick someone in your group to join Discord.
+It's fine if multiple people join, but one is enough.
+
+
Now switch to Pensieve:
+
+
+
Everyone: Go to discuss.pensieve.co and log in with your @berkeley.edu email, then enter your group number. (Your group number is the number of your Discord channel.)
+
+
+
Once you're on Pensieve, you don't need to return to this page; Pensieve has all the same content (but more features). If for some reason Penseive doesn't work, return to this page and continue with the discussion.
+
+
Post in the #help channel on Discord if you have trouble.
+
+
+
Getting Started
+
+
+
If you have only 1 or 2 people in your group, you can join the other group in
+the room with you.
+
+
Everybody say your name and your birthday and then tell the group about your
+favorite birthday party you've attended (either for your birthday or someone
+else's).
+
+
Pro tip: Groups tend not to ask for help unless they've been stuck for a
+looooooong time. Try asking for help sooner. We're pretty helpful! You might
+learn something.
+
+
+
Linked Lists
+
+
+
A linked list is a Link object or Link.empty.
+
+
You can mutate a Link object s in two ways:
+
+
+
Change the first element with s.first = ...
+
Change the rest of the elements with s.rest = ...
+
+
+
You can make a new Link object by calling Link:
+
+
+
Link(4) makes a linked list of length 1 containing 4.
+
Link(4, s) makes a linked list that starts with 4 followed by the elements of linked list s.
+
+
+
+
+
+
+
class Link:
+ """A linked list is either a Link object or Link.empty
+
+ >>> s = Link(3, Link(4, Link(5)))
+ >>> s.rest
+ Link(4, Link(5))
+ >>> s.rest.rest.rest is Link.empty
+ True
+ >>> s.rest.first * 2
+ 8
+ >>> print(s)
+ <3 4 5>
+ """
+ empty = ()
+
+ def __init__(self, first, rest=empty):
+ assert rest is Link.empty or isinstance(rest, Link)
+ self.first = first
+ self.rest = rest
+
+ def __repr__(self):
+ if self.rest:
+ rest_repr = ', ' + repr(self.rest)
+ else:
+ rest_repr = ''
+ return 'Link(' + repr(self.first) + rest_repr + ')'
+
+ def __str__(self):
+ string = '<'
+ while self.rest is not Link.empty:
+ string += str(self.first) + ' '
+ self = self.rest
+ return string + str(self.first) + '>'
+
+
+
+
+
Facilitator: Pick a way for your group to draw diagrams. Paper, a
+whiteboard, or a tablet, are all fine. If you don't have anything like that, ask
+the other group in the room if they have extra paper.
+
+
+
Q1: Strange Loop
+
+
+
In lab, there was a Link object with a cycle that represented an infinite repeating list of 1's.
Implement strange_loop, which takes no arguments and returns a Link object
+s for which s.rest.first.rest is s.
+
+
Draw a picture of the linked list you want to create, then write code to create
+it.
+
+
Facilitator: When you think everyone has had a chance to read this far,
+please say: "So, what is this thing going to look like?"
+
+
+
+
+For s.rest.first.rest to exist at all, the second element of s, called
+s.rest.first, must itself be a linked list.
+
+
+
+
+
+
+
+
+
+
+
+Making a cycle requires two steps: making
+a linked list without a cycle, then modifying it. First create, for example, s
+= Link(6, Link(Link(1))), then change s.rest.first.rest to create the cycle.
+
def strange_loop():
+ """Return a Link s for which s.rest.first.rest is s.
+
+ >>> s = strange_loop()
+ >>> s.rest.first.rest is s
+ True
+ """
+ s = Link(1, Link(Link(2)))
+ s.rest.first.rest = s
+ return s
+
+
+
+
+
+
+
+
+
Q2: Sum Two Ways
+
+
+
Implement both sum_rec and sum_iter. Each one takes a linked list of numbers
+s and returns the sum of its elements. Use recursion to implement sum_rec.
+Don't use recursion to implement sum_iter; use a while loop instead.
def sum_rec(s):
+ """
+ Returns the sum of the elements in s.
+
+ >>> a = Link(1, Link(6, Link(7)))
+ >>> sum_rec(a)
+ 14
+ >>> sum_rec(Link.empty)
+ 0
+ """
+ # Use a recursive call to sum_rec
+ if s == Link.empty:
+ return 0
+ return s.first + sum_rec(s.rest)
+
+def sum_iter(s):
+ """
+ Returns the sum of the elements in s.
+
+ >>> a = Link(1, Link(6, Link(7)))
+ >>> sum_iter(a)
+ 14
+ >>> sum_iter(Link.empty)
+ 0
+ """
+ # Don't call sum_rec or sum_iter
+ total = 0
+ while s != Link.empty:
+ total, s = total + s.first, s.rest
+ return total
+
+
+
+
+
+
+
+
+Add s.first to the sum of the elements in s.rest. Your base case condition
+should be s is Link.empty so that you're checking whether s is empty before
+ever evaluating s.first or s.rest.
+
+
+
+
+
+Introduce a new name, such as total, then repeatedly (in a while loop) add
+s.first to total and set s = s.rest to advance through the linked list, as
+long as s is not Link.empty.
+
+
+
Discussion time: When adding up numbers, the intermediate sums depend on the
+order.
+
+
(1 + 3) + 5 and 1 + (3 + 5) both equal 9, but the first one makes 4 along
+the way while the second makes 8 along the way. For the same linked list, will
+sum_rec and sum_iter both make the same intermediate sums along the way?
+Answer in your group's Discord channel's text chat.
+If yes, post "Same way all day." If no, post "Sum thing is different."
+
+
+
+
+
Q3: Overlap
+
+
+
Implement overlap, which takes two linked lists of numbers called s and t
+that are sorted in increasing order and have no repeated elements within each
+list. It returns the count of how many numbers appear in both lists.
+
+
This can be done in linear time in the combined length of s and t by
+always advancing forward in the linked list whose first element is smallest
+until both first elements are equal (add one to the count and advance both) or
+one list is empty (time to return). Here's a
+lecture video clip
+about this (but the video uses Python lists instead of linked lists).
+
+
Take a vote to decide whether to use recursion or iteration.
+Either way works (and the solutions are about the same complexity/difficulty).
def overlap(s, t):
+ """For increasing s and t, count the numbers that appear in both.
+
+ >>> a = Link(3, Link(4, Link(6, Link(7, Link(9, Link(10))))))
+ >>> b = Link(1, Link(3, Link(5, Link(7, Link(8)))))
+ >>> overlap(a, b) # 3 and 7
+ 2
+ >>> overlap(a.rest, b) # just 7
+ 1
+ >>> overlap(Link(0, a), Link(0, b))
+ 3
+ """
+ if s is Link.empty or t is Link.empty:
+ return 0
+ if s.first == t.first:
+ return 1 + overlap(s.rest, t.rest)
+ elif s.first < t.first:
+ return overlap(s.rest, t)
+ elif s.first > t.first:
+ return overlap(s, t.rest)
+
+def overlap_iterative(s, t):
+ """For increasing s and t, count the numbers that appear in both.
+
+ >>> a = Link(3, Link(4, Link(6, Link(7, Link(9, Link(10))))))
+ >>> b = Link(1, Link(3, Link(5, Link(7, Link(8)))))
+ >>> overlap(a, b) # 3 and 7
+ 2
+ >>> overlap(a.rest, b) # just 7
+ 1
+ >>> overlap(Link(0, a), Link(0, b))
+ 3
+ """
+ res = 0
+ while s is not Link.empty and t is not Link.empty:
+ if s.first == t.first:
+ res += 1
+ s = s.rest
+ t = t.rest
+ elif s.first < t.first:
+ s = s.rest
+ else:
+ t = t.rest
+ return res
+
+
+
+
+
+
+
+
+
+
if s is Link.empty or t is Link.empty:
+ return 0
+ if s.first == t.first:
+ return __________________
+ elif s.first < t.first:
+ return __________________
+ elif s.first > t.first:
+ return __________________
+
+
+
+
+
+
+
+
+
k = 0
+ while s is not Link.empty and t is not Link.empty:
+ if s.first == t.first:
+ __________________
+ elif s.first < t.first:
+ __________________
+ elif s.first > t.first:
+ __________________
+ return k
+
+
+
+
+
+
+
+
+
+
+
+
Document the Occasion
+
+
+
Please all fill out the attendance form (one submission per person per week).
+
+
Important: Please help put the furniture in the room back where you found it before you leave. Thanks!
+
+
+
Extra Challenge
+
+
+
This last question is similar in complexity to an A+ question on an exam. Feel free to skip it, but it's a fun one, so try it if you have time.
+
+
+
Q4: Decimal Expansion
+
+
+
Definition. The decimal expansion of a fraction n/d with n < d is an
+infinite sequence of digits starting with the 0 before the decimal point and
+followed by digits that represent the tenths, hundredths, and thousands place
+(and so on) of the number n/d. E.g., the decimal expansion of 2/3 is a zero
+followed by an infinite sequence of 6's: 0.6666666....
+
+
Implement divide, which takes positive integers n and d with n < d. It
+returns a linked list with a cycle containing the digits of the infinite decimal
+expansion of n/d. The provided display function prints the first k digits
+after the decimal point.
+
+
For example, 1/22 would be represented as x below:
def display(s, k=10):
+ """Print the first k digits of infinite linked list s as a decimal.
+
+ >>> s = Link(0, Link(8, Link(3)))
+ >>> s.rest.rest.rest = s.rest.rest
+ >>> display(s)
+ 0.8333333333...
+ """
+ assert s.first == 0, f'{s.first} is not 0'
+ digits = f'{s.first}.'
+ s = s.rest
+ for _ in range(k):
+ assert s.first >= 0 and s.first < 10, f'{s.first} is not a digit'
+ digits += str(s.first)
+ s = s.rest
+ print(digits + '...')
def divide(n, d):
+ """Return a linked list with a cycle containing the digits of n/d.
+
+ >>> display(divide(5, 6))
+ 0.8333333333...
+ >>> display(divide(2, 7))
+ 0.2857142857...
+ >>> display(divide(1, 2500))
+ 0.0004000000...
+ >>> display(divide(3, 11))
+ 0.2727272727...
+ >>> display(divide(3, 99))
+ 0.0303030303...
+ >>> display(divide(2, 31), 50)
+ 0.06451612903225806451612903225806451612903225806451...
+ """
+ assert n > 0 and n < d
+ result = Link(0) # The zero before the decimal point
+ cache = {}
+ tail = result
+ while n not in cache:
+ q, r = 10 * n // d, 10 * n % d
+ tail.rest = Link(q)
+ tail = tail.rest
+ cache[n] = tail
+ n = r
+ tail.rest = cache[n]
+ return result
+
+
+
+
+
+
+
+Place the division pattern from the example above in a while statement:
+
+
>>> q, r = 10 * n // d, 10 * n % d
+>>> tail.rest = Link(q)
+>>> tail = tail.rest
+>>> n = r
+
+
+
+
While constructing the decimal expansion, store the tail for each n in a
+dictionary keyed by n. When some n appears a second time, instead of
+constructing a new Link, set its original link as the rest of the previous
+link. That will form a cycle of the appropriate length.
+
Reminder: We'll still use Pensieve, but we've removed the voice/video chat from Pensieve. Use Discord for voice chat with the course staff. It's more reliable and includes screensharing. Write to @discuss in the #discuss-queue channel on Discord at any time, and a member of the course staff will join your group's voice channel.
+
+
Pick someone in your group to join Discord.
+It's fine if multiple people join, but one is enough.
+
+
Now switch to Pensieve:
+
+
+
Everyone: Go to discuss.pensieve.co and log in with your @berkeley.edu email, then enter your group number. (Your group number is the number of your Discord channel.)
+
+
+
Once you're on Pensieve, you don't need to return to this page; Pensieve has all the same content (but more features). If for some reason Penseive doesn't work, return to this page and continue with the discussion.
+
+
Post in the #help channel on Discord if you have trouble.
+
+
Pro tip: Any of you can type a question into your group's Discord channel's text chat with the @discuss tag, and a member of the course staff will respond.
+
+
+
Getting Started
+
+
+
If you have only 1 or 2 people in your group, you can join the other group in
+the room with you.
+
+
Everybody say your name, and then figure out who most recently pet a dog. (Feel
+free to share dog photos. Even cat photos are acceptable.)
+
+
+
Scheme
+
+
+
+
Q1: Perfect Fit
+
+
+
Definition: A perfect square is k*k for some integer k.
+
+
Implement fit, which takes non-negative integers total and n. It returns
+whether there are ndifferent positive perfect squares that sum to
+total.
+
+
Important: Don't use the Scheme interpreter to tell you whether you've
+implemented it correctly. Discuss! On the final exam, you won't have an
+interpreter.
; Return whether there are n perfect squares with no repeats that sum to total
+
+ (define (fit total n)
+ (define (f total n k)
+ (if (and (= n 0) (= total 0))
+ #t
+ (if (< total (* k k))
+ #f
+ (or (f total n (+ k 1)) (f (- total (* k k)) (- n 1) (+ k 1)))
+ )))
+ (f total n 1))
+
+ (expect (fit 10 2) #t) ; 1*1 + 3*3
+ (expect (fit 9 1) #t) ; 3*3
+ (expect (fit 9 2) #f) ;
+ (expect (fit 9 3) #f) ; 1*1 + 2*2 + 2*2 doesn't count because of repeated 2*2
+ (expect (fit 25 1) #t) ; 5*5
+ (expect (fit 25 2) #t) ; 3*3 + 4*4
+
+
+
+
+
+
+
+
+
Use the (or _ _) special form to combine two recursive calls: one that uses
+k*k in the sum and one that does not. The first should subtract k*k from
+total and subtract 1 from n; the other should leaves total and n
+unchanged.
+
+
+
+
Presentation Time: As a group, come up with one sentence describing how your implementation makes sure that all n positive perfect squares are different (no repeats). Once your group agrees on an answer (or wants
+help), send a message to the #discuss-queue channel with the @discuss tag,
+your discussion group number, and the message "It fits!" and a member of the
+course staff will join your voice channel to hear your explanation and give
+feedback.
+
+
+
+
+
Scheme Lists & Quotation
+
+
+
Scheme lists are linked lists. Lightning review:
+
+
+
nil and () are the same thing: the empty list.
+
(cons first rest) constructs a linked list with first as its first element.
+ and rest as the rest of the list, which should always be a list.
+
(car s) returns the first element of the list s.
+
(cdr s) returns the rest of the list s.
+
(list ...) takes n arguments and returns a list of length n with those arguments as elements.
+
(append ...) takes n lists as arguments and returns a list of all of the elements of those lists.
+
(draw s) draws the linked list structure of a list s. It only works on
+ code.cs61a.org/scheme. Try it now with something like (draw (cons 1 nil)).
+
+
+
Quoting an expression leaves it unevaluated. Examples:
+
+
+
'four and (quote four) both evaluate to the symbol four.
+
'(2 3 4) and (quote (2 3 4)) both evaluate to a list containing three elements: 2, 3, and 4.
+
'(2 3 four) and (quote (2 3 four)) evaluate to a list containing 2, 3, and the symbol four.
+
+
+
Here's an important difference between list and quotation:
Create the nested list depicted below three different ways: using list, quote, and cons.
+
+
+
+
+
First, describe the list together: "It looks like there are four elements, and the first element is ..." If you get stuck, look at the hint below. (But try to describe it yourself first!)
+
+
+
+
+A four-element list in which the first element is a list containing both a and
+b, the second element is c, the third element is d, and the fourth element
+is a list containing just e.
+
+
+
Next, use calls to list to construct this list. If you run this code and then (draw with-list) in
+code.cs61a.org, the draw procedure will draw what you've built.
(define with-list
+ (list
+ (list 'a 'b) 'c 'd (list 'e)
+ )
+ )
+ ; (draw with-list) ; Uncomment this line to draw with-list
+
+
+
+
+
+
+
+Every call to list creates a list, and there are three different lists in this
+diagram: a list containing a and b: (list 'a 'b), a list containing e:
+(list 'e), and the whole list of four elements: (list _ 'c 'd _). Try to
+put these expressions together.
+
(define with-quote
+ '(
+ (a b) c d (e)
+ )
+
+ )
+ ; (draw with-quote) ; Uncomment this line to draw with-quote
+
+
+
+
+
+
+
+One quoted expression is enough, but it needs to match the structure of the
+linked list using Scheme notation. So, your task is to figure out how this list
+would be displayed in Scheme.
+
+
+
+
+
+The nested list drawn above is a four-element list with lists as its first and
+last elements: ((a b) c d (e)). Quoting that expression will create the list.
+
+
+
Now, use cons to construct this list. Don't use list. You can use first in your answer.
(define with-cons
+ (cons
+ first (cons 'c (cons 'd (cons (cons 'e nil) nil)))
+ )
+ )
+ ; (draw with-cons) ; Uncomment this line to draw with-cons
+
+
+
+
+
+
+
+The provided first is the first element of the result, so the answer takes the form:
+
+
first ____
+
+
+
+
You can either fill in the blank with a quoted three-element list:
+
+
'(___ ___ ___)
+ c d (e)
+
+
+
+
or with nested calls to cons:
+
+
(cons ___ (cons ___ (cons ___ nil)))
+ c d (e)
+
+
+
+
+
+
+
+
+
Q3: Pair Up
+
+
+
Implement pair-up, which takes a list s. It returns a list of lists that
+together contain all of the elements of s in order. Each list in the result
+should have 2 elements. The last one can have up to 3.
+
+
Look at the examples together to make sure everyone understands what this procedure does.
pair-up takes a list (of numbers) and returns a list of lists, so when
+(length s) is less than or equal to 3, return a list containing the list s.
+For example, (pair-up (list 2 3 4)) should return ((2 3 4)).
+
+
+
+
+
+
+
+
Use (cons _ (pair-up _)) to create the result, where the first argument to
+cons is a list with two elements: the (car s) and the (car (cdr s)). The
+argument to pair-up is everything after the first two elements.
+
+
+
+
Discussion: What's the longest list s for which (pair-up (pair-up s))
+will return a list with only one element? (Don't just guess and check; discuss!)
+Post your answer in your group's text chat.
+
+
+
+
+
Document the Occasion
+
+
+
Please all fill out the attendance form (one submission per person per week).
+
+
Important: Please help put the furniture in the room back where you found it before you leave. Thanks!
Reminder: We'll still use Pensieve, but we've removed the voice/video chat from Pensieve. Use Discord for voice chat with the course staff. It's more reliable and includes screensharing. Write to @discuss in the #discuss-queue channel on Discord at any time, and a member of the course staff will join your group's voice channel.
+
+
Pick someone in your group to join Discord.
+It's fine if multiple people join, but one is enough.
+
+
Now switch to Pensieve:
+
+
+
Everyone: Go to discuss.pensieve.co and log in with your @berkeley.edu email, then enter your group number. (Your group number is the number of your Discord channel.)
+
+
+
Once you're on Pensieve, you don't need to return to this page; Pensieve has all the same content (but more features). If for some reason Penseive doesn't work, return to this page and continue with the discussion.
+
+
Post in the #help channel on Discord if you have trouble.
+
+
Pro tip: Any of you can type a question into your group's Discord channel's text chat with the @discuss tag, and a member of the course staff will respond.
+
+
+
Getting Started
+
+
+
If you have only 1 or 2 people in your group, you can join the other group in
+the room with you.
+
+
Everybody say your name, and then figure out who is planning to travel outside
+of the Bay Area the soonest.
+
+
+
Representing Lists
+
+
+
A Scheme call expression is a Scheme list that is represented using a Pair
+instance in Python.
+
+
For example, the call expression (+ (* 3 4) 5) is represented as:
class Pair:
+ "A Scheme list is a Pair in which rest is a Pair or nil."
+ def __init__(self, first, rest):
+ self.first = first
+ self.rest = rest
+
+ ... # There are also __str__, __repr__, and map methods, omitted here.
+
+
+
+
+
Q1: Representing Expressions
+
+
Write the Scheme expression in Scheme syntax represented by each Pair below.
+Try drawing the linked list diagram too. The first one is done for you.
Discussion Time: What does (and (< 1 0) (/ 1 0)) evaluate to? Discuss among
+your group until you all agree,
+then post your answer in your group's Discord
+channel's text chat.
+
+
+
Evaluation
+
+
+
To evaluate the expression (+ (* 3 4) 5) using the Project 4 interpreter,
+scheme_eval is called on the following expressions (in this order):
+
+
+
(+ (* 3 4) 5)
+
+
+
(* 3 4)
+
*
+
3
+
4
+
5
+
+
+
Discussion time: Describe to each other why * is evaluated and what it
+evaluates to.
+
+
+
+
+
+
The * is evaluated because it is the operator sub-expression of (* 3 4),
+which is an operand sub-expression of (+ (* 3 4) 5).
+
+
By default, * evaluates to a procedure that multiplies its arguments together.
+But * could be redefined at any time, and so the symbol * must be evaluated
+each time it is used in order to look up its current value.
+
+
scm> (* 2 3) ; Now it multiplies
+6
+scm> (define * +)
+*
+scm> (* 2 3) ; Now it adds
+5
+
+
+
+
+
+
An if expression is also a Scheme list represented using a Pair instance.
+
+
For example, (if (< x 0) (- x) x) is represented as:
To evaluate this expression in an environment in which x is bound to 2 (and
+< and - have their default values), scheme_eval is called on the following
+expressions (in this order):
+
+
+
(if (< x 0) (- x) x)
+
(< x 0)
+
<
+
x
+
0
+
x
+
+
+
Presentation time: Come up with a short explanation of why neither if nor
+- are evaluated even though they both appear in (if (< x 0) (- x) x). Once
+your group agrees on an answer (or wants help), send a message to the
+#discuss-queue channel with the @discuss tag, your discussion group number,
+and the message "If you please!" and a member of the course staff will join your
+voice channel to hear your explanation and give feedback.
+
+
+
+
The symbol if is not evaluated because it is the start of a special form, not
+part of a call expression. The symbols that introduce special forms (and,
+if, lambda, etc.) are never evaluated.
+
+
The symbol - is not evaluated, nor is the whole sub-expression (- x) that it
+appears in, because (< x 0) evaluates to #f. If you're still not certain
+why some parts are evaluated and some aren't, ask the course staff.
+
+
+
+
+
Q2: Evaluation
+
+
Which of the following are evaluated when scheme_eval is called on
+(if (< x 0) (- x) (if (= x -2) 100 y)) in an environment in which x is bound to -2?
+(Assume <, -, and = have their default values.)
+
+
+
if
+
<
+
=
+
x
+
y
+
0
+
-2
+
100
+
-
+
(
+
)
+
+
+
+
+
+
+
With x bound to -2, (< x 0) evaluates to #t, and so (- x) will be
+evaluated, but (if (= x 1) 100 x) will not. The operator and operands of a
+call expression are evaluated for every call expression that is evaluated.
+(< x 0) and (- x) are both call expressions.
+
+
+
+
List your group's answers in your group's
+Discord channel's text chat.
+For example, write if < 0 ) if you think those are the four that get evaluated.
+A member of the course staff will review your answer and give feedback.
+
+
+
Q3: Print Evaluated Expressions
+
+
+
Define print_evals, which takes a Scheme expression expr that contains only
+numbers, +, *, and parentheses. It prints all of the expressions that are
+evaluated during the evaluation of expr. They are printed in the order that
+they are passed to scheme_eval.
+
+
Note: Calling print on a Pair instance will print the Scheme expression it represents.
If expr is not a pair, then it is a number or '+' or '*'. In all of these
+cases, the expr should be printed to indicate that it would be evaluated.
+
+
+
+
+
+
+
+
If expr is a pair, then it is a call expression. Print it. Then, the operator
+and operands are evaluated. These are the elements in the list expr. So,
+iterate through expr (using either a while statement or expr.map(...)) and
+call print_evals on each element.
+
+
+
+
+
Document the Occasion
+
+
+
Please all fill out the attendance form (one submission per person per week).
+
+
Important: Please help put the furniture in the room back where you found it before you leave. Thanks!
Reminder: Use Discord for voice chat with the course staff. Write to @discuss in the #discuss-queue channel on Discord at any time, and a member of the course staff will join your group's voice channel.
+
+
Pick someone in your group to join Discord.
+It's fine if multiple people join, but one is enough.
+
+
Now switch to Pensieve:
+
+
+
Everyone: Go to discuss.pensieve.co and log in with your @berkeley.edu email, then enter your group number. (Your group number is the number of your Discord channel.)
+
+
+
Once you're on Pensieve, you don't need to return to this page; Pensieve has all the same content (but more features). If for some reason Penseive doesn't work, return to this page and continue with the discussion.
+
+
Post in the #help channel on Discord if you have trouble.
+
+
Pro tip: Any of you can type a question into your group's Discord channel's text chat with the @discuss tag, and a member of the course staff will respond.
+
+
+
Getting Started
+
+
+
If you have only 1 or 2 people in your group, you can join the other group in
+the room with you.
+
+
Everybody say your name, and then share your favorite restaurant, cafe, or boba shop near campus. (Yes, Kingpin Donuts counts as a restaurant.)
+
+
+
Select Statements
+
+
+
A SELECT statement describes an output table based on input rows. To write one:
+
+
+
Describe the input rows using FROM and WHERE clauses.
+
Format and order the output rows and columns using SELECT and ORDER BY clauses.
Step 1 may involve joining tables (using commas) to form input rows that consist of two or more rows from existing tables.
+
+
The WHERE and ORDER BY clauses are optional.
+
+
+
Pizza Time
+
+
+
The pizzas table contains the names, opening, and closing hours of great pizza
+places in Berkeley. The meals table contains typical meal times (for college
+students). A pizza place is open for a meal if the meal time is at or within the
+open and close times.
+
+
CREATE TABLE pizzas AS
+ SELECT "Artichoke" AS name, 12 AS open, 15 AS close UNION
+ SELECT "La Val's" , 11 , 22 UNION
+ SELECT "Sliver" , 11 , 20 UNION
+ SELECT "Cheeseboard" , 16 , 23 UNION
+ SELECT "Emilia's" , 13 , 18;
+
+CREATE TABLE meals AS
+ SELECT "breakfast" AS meal, 11 AS time UNION
+ SELECT "lunch" , 13 UNION
+ SELECT "dinner" , 19 UNION
+ SELECT "snack" , 22;
+
+
+
+
+
Q1: Open Early
+
+
+
You'd like to have pizza before 13 o'clock (1pm). Create a opening table with
+the names of all pizza places that open before 13 o'clock, listed in reverse
+alphabetical order.
-- Pizza places that open before 1pm in alphabetical order
+CREATE TABLE opening AS
+ SELECT name FROM pizzas WHERE open < 13 ORDER BY name DESC;
+
+
+
+
+
+
+
+
+To order by name in reverse alphabitical order, write ORDER BY name DESC.
+
+
+
+
+
+
Q2: Study Session
+
+
+
You're planning to study at a pizza place from the moment it opens until 14
+o'clock (2pm). Create a table study with two columns, the name of each pizza
+place and the duration of the study session you would have if you studied there
+(the difference between when it opens and 14 o'clock). For pizza places that are
+not open before 2pm, the duration should be zero. Order the
+rows by decreasing duration.
+
+
Hint: Use an expression of the form MAX(_, 0) to make sure a result is not below 0.
-- Pizza places and the duration of a study break that ends at 14 o'clock
+CREATE TABLE study AS
+ SELECT name, MAX(14 - open, 0) AS duration FROM pizzas ORDER BY duration DESC;
+
+
+
+
+
+
+
+
+To order by decreasing duration, first name the column with SELECT ..., ... AS duration ..., then ORDER BY duration DESC.
+
+
+
+
Q3: Late Night Snack
+
+
+
What's still open for a late night snack? Create a late table with one
+column named status that has a sentence describing the closing time of each
+pizza place that closes at or after snack time. Important: Don't use any
+numbers in your SQL query! Instead, use a join to compare each restaurant's
+closing time to the time of a snack. The rows may appear in any order.
-- Pizza places that are open for late-night-snack time and when they close
+CREATE TABLE late AS
+ SELECT name || " closes at " || close AS status FROM pizzas, meals WHERE meal="snack" AND time<=clos;
+
+
+
+
+
+
+
+
+To compare a pizza place's close time to the time of a snack:
+
+
+
join the pizzas and meals tables using FROM pizzas, meals
+
use only rows where the meal is a "snack"
+
compare the time of the snack to the close of the pizza place.
+
+
+
+
+
+
+Use name || " closes at " || close to create the sentences in the resulting table. The || operator concatenates values into strings.
+
+
+
+
Q4: Double Pizza
+
+
+
If two meals are more than 6 hours apart, then there's nothing wrong with going
+to the same pizza place for both, right? Create a double table with three
+columns. The first column is the earlier meal, the second column is the
+later meal, and the name column is the name of a pizza place. Only include
+rows that describe two meals that are more than 6 hours apart and a pizza
+place that is open for both of the meals. The rows may appear in any order.
-- Two meals at the same place
+CREATE TABLE double AS
+ SELECT a.meal AS first, b.meal AS second, name
+ FROM meals AS a, meals AS b, pizzas
+ WHERE open <= a.time AND a.time <= close AND
+ open <= b.time AND b.time <= close AND
+ b.time > a.time + 6;
+
+
+
+
+
+
+
+
+Use FROM meals AS a, meals AS b, pizzas so that each row has info about two meals and a pizza place. Then you can write a WHERE clause that compares both a.time and b.time to open and close and each other to ensure all the relevant conditions are met.
+
+
+
+
+
+
+
+
+
+
Document the Occasion
+
+
+
Please all fill out the attendance form (one submission per person per week).
+
+
Important: Please help put the furniture in the room back where you found it before you leave. Thanks!
+
+
If you finish early, maybe go get pizza together...
Reminder: Use Discord for voice chat with the course staff. Write to @discuss in the #discuss-queue channel on Discord at any time, and a member of the course staff will join your group's voice channel.
+
+
Pick someone in your group to join Discord.
+It's fine if multiple people join, but one is enough.
+
+
Now switch to Pensieve:
+
+
+
Everyone: Go to discuss.pensieve.co and log in with your @berkeley.edu email, then enter your group number. (Your group number is the number of your Discord channel.)
+
+
+
Once you're on Pensieve, you don't need to return to this page; Pensieve has all the same content (but more features). If for some reason Penseive doesn't work, return to this page and continue with the discussion.
+
+
Post in the #help channel on Discord if you have trouble.
+
+
Pro tip: Any of you can type a question into your group's Discord channel's text chat with the @discuss tag, and a member of the course staff will respond.
+
+
+
Getting Started
+
+
+
If you have only 1 or 2 people in your group, you can join the other group in
+the room with you.
+
+
Ice breaker: Everybody say your name and the non-CS/EECS course that you're
+most excited about taking next semester.
+
+
+
Lists
+
+
+
+
+
+The two most common mutation operations for lists are item assignment and the
+append method.
+
+
>>> s = [1, 3, 4]
+>>> t = s # A second name for the same list
+>>> t[0] = 2 # this changes the first element of the list to 2, affecting both s and t
+>>> s
+[2, 3, 4]
+>>> s.append(5) # this adds 5 to the end of the list, affecting both s and t
+>>> t
+[2, 3, 4, 5]
+
+
There are many other list mutation methods:
+
+
+
append(elem):
+ Add elem to the end of the list. Return None.
+
extend(s):
+ Add all elements of iterable s to the end of the list. Return None.
+
insert(i, elem):
+ Insert elem at index i. If i is greater than or equal to the length of
+ the list, then elem is inserted at the end. This does not replace any
+ existing elements, but only adds the new element elem. Return None.
+
remove(elem):
+ Remove the first occurrence of elem in list. Return None.
+ Errors if elem is not in the list.
+
pop(i):
+ Remove and return the element at index i.
+
pop():
+ Remove and return the last element.
+
+
+
+
+
Q1: Word Rope
+
+
+
Definition: A rope in Python is a list containing only one-letter strings
+except for the last element, which may either be a one-letter string or a rope.
+
+
Implement word_rope, a Python function that takes a non-empty string s
+containing only letters and spaces that does not start or end with a space. It
+returns a rope containing the letters of s in which each word is in a
+separate list.
+
+
Important: You may not use slicing or the split, find, or index
+methods of a string. Solve the problem using list operations.
+
+
Reminder:s[-1] evaluates to the last element of a sequence s.
def word_rope(s):
+ """Return a rope of the words in string s.
+
+ >>> word_rope('the last week')
+ ['t', 'h', 'e', ['l', 'a', 's', 't', ['w', 'e', 'e', 'k']]]
+ """
+ assert s and s[0] != ' ' and s[-1] != [ ]
+ result = []
+ word = result
+ for x in s:
+ if x == ' ':
+ word.append([])
+ word = word[-1]
+ else:
+ word.append(x)
+ return result
+
+
+
+
+
+
+
+In this implementation, result is a rope and word is a list within that rope which is still being constructed. When x is a space, add an empty list to the end of word and assign word to this empty list. Otherwise, add x to the end of word.
+
+
+
+
Linked Lists
+
+
+
+
+
+A linked list is a Link object or Link.empty.
+
+
You can mutate a Link object s in two ways:
+
+
+
Change the first element with s.first = ...
+
Change the rest of the elements with s.rest = ...
+
+
+
You can make a new Link object by calling Link:
+
+
+
Link(4) makes a linked list of length 1 containing 4.
+
Link(4, s) makes a linked list that starts with 4 followed by the elements of linked list s.
+
+
+
+
+
+
+
+
class Link:
+ """A linked list is either a Link object or Link.empty
+
+ >>> s = Link(3, Link(4, Link(5)))
+ >>> s.rest
+ Link(4, Link(5))
+ >>> s.rest.rest.rest is Link.empty
+ True
+ >>> s.rest.first * 2
+ 8
+ >>> print(s)
+ <3 4 5>
+ """
+ empty = ()
+
+ def __init__(self, first, rest=empty):
+ assert rest is Link.empty or isinstance(rest, Link)
+ self.first = first
+ self.rest = rest
+
+ def __repr__(self):
+ if self.rest:
+ rest_repr = ', ' + repr(self.rest)
+ else:
+ rest_repr = ''
+ return 'Link(' + repr(self.first) + rest_repr + ')'
+
+ def __str__(self):
+ string = '<'
+ while self.rest is not Link.empty:
+ string += str(self.first) + ' '
+ self = self.rest
+ return string + str(self.first) + '>'
+
+
+
+
+
+
+
Q2: Linear Sublists
+
+
+
Definition: A sublist of linked list s is a linked list of some of the
+elements of s in order. For example, <3 6 2 5 1 7> has sublists <3 2 1>
+and <6 2 7> but not <5 6 7>.
+
+
Definition: A linear sublist of a linked list of numbers s is a sublist
+in which the difference between adjacent numbers is always the same. For example
+<2 4 6 8> is a linear sublist of <1 2 3 4 6 9 1 8 5> because the difference
+between each pair of adjacent elements is 2.
+
+
Implement linear which takes a linked list of numbers s (either a Link
+instance or Link.empty). It returns the longest linear sublist of s. If two
+linear sublists are tied for the longest, return either one.
def linear(s):
+ """Return the longest linear sublist of a linked list s.
+
+ >>> s = Link(9, Link(4, Link(6, Link(7, Link(8, Link(10))))))
+ >>> linear(s)
+ Link(4, Link(6, Link(8, Link(10))))
+ >>> linear(Link(4, Link(5, s)))
+ Link(4, Link(5, Link(6, Link(7, Link(8)))))
+ >>> linear(Link(4, Link(5, Link(4, Link(7, Link(3, Link(2, Link(8))))))))
+ Link(5, Link(4, Link(3, Link(2))))
+ """
+ def complete(first, rest):
+ "The longest linear sublist of Link(first, rest) with difference d."
+ if rest is Link.empty:
+ return Link(first, rest)
+ elif rest.first - first == d:
+ return Link(first, complete(rest.first, rest.rest))
+ else:
+ return complete(first, rest.rest)
+ if s is Link.empty:
+ return s
+ longest = Link(s.first) # The longest linear sublist found so far
+ while s is not Link.empty:
+ t = s.rest
+ while t is not Link.empty:
+ d = t.first - s.first
+ candidate = Link(s.first, complete(t.first, t.rest))
+ if length(candidate) > length(longest):
+ longest = candidate
+ t = t.rest
+ s = s.rest
+ return longest
+
+def length(s):
+ if s is Link.empty:
+ return 0
+ else:
+ return 1 + length(s.rest)
+
+
+
+
+
+
+
+There are three cases:
+
+
+
If rest is empty, return a one-element list containing just first.
+
If rest.first is in the linear sublist that starts with first, then build a list that contains first, and rest.first.
+
Otherwise, complete(first, rest.rest).
+
+
+
+
+
+
+This while loop is creating a candidate linear sublist for every two possible starting values: s.first and t.first. The rest of the linear sublist must be in t.rest.
+
+
+
+
Scheme
+
+
+
+
Q3: Increasing Rope
+
+
+
Definition: A rope in Scheme is a non-empty list containing only numbers
+except for the last element, which may either be a number or a rope.
+
+
Implement up, a Scheme procedure that takes a positive integer n. It returns
+a rope containing the digits of n that is the shortest rope in which each pair
+of adjacent numbers in the same list are in increasing order.
+
+
Reminder: the quotient procedure performs floor division, like // in
+Python. The remainder procedure is like % in Python.
(define (up n)
+ (define (helper n result)
+ (if (zero? n) result
+ (helper
+ (quotient n 10)
+ (let ((first (remainder n 10)))
+ (if (< first (car result))
+ (cons first result)
+ (list first result))
+ ))))
+ (helper
+ (quotient n 10)
+ (list (remainder n 10))
+ ))
+
+ (expect (up 314152667899) (3 (1 4 (1 5 (2 6 (6 7 8 9 (9)))))))
+
+
+
+
+
+
+
+Compare first to (car result) to decide whether to cons the value first onto the result or whether to form a new list that contains first and result as elements.
+
+
+
+
+
+To correctly call helper from within up, build a rope that only contains the last digit of n: (remainder n 10).
+
+
+
+
SQL
+
+
+
+
+
+A SELECT statement describes an output table based on input rows. To write one:
+
+
+
Describe the input rows using FROM and WHERE clauses.
+
Group those rows and determine which groups should appear as output rows using GROUP BY and HAVING clauses.
+
Format and order the output rows and columns using SELECT and ORDER BY clauses.
Step 1 may involve joining tables (using commas) to form input rows that consist of two or more rows from existing tables.
+
+
The WHERE, GROUP BY, HAVING, and ORDER BY clauses are optional.
+
+
+
+
Q4: A Secret Message
+
+
+
A substitution cipher replaces each word with another word in a table in order
+to encrypt a message. To decode an encrypted message, replace each word x with
+its corresponding y in a code table.
+
+
Write a select statement to decode the original message It's The End
+using the code table.
CREATE TABLE original AS
+ SELECT 1 AS n, "It's" AS word UNION
+ SELECT 2 , "The" UNION
+ SELECT 3 , "End";
+
+CREATE TABLE code AS
+ SELECT "Up" AS x, "Down" AS y UNION
+ SELECT "Now" , "Home" UNION
+ SELECT "It's" , "What" UNION
+ SELECT "See" , "Do" UNION
+ SELECT "Can" , "See" UNION
+ SELECT "End" , "Now" UNION
+ SELECT "What" , "You" UNION
+ SELECT "The" , "Happens" UNION
+ SELECT "Love" , "Scheme" UNION
+ SELECT "Not" , "Mess" UNION
+ SELECT "Happens", "Go";
+
+SELECT y FROM original, code WHERE word=x ORDER BY n;
+
+
+
+
+
+
+
+
+
+
Join the original and code tables and make sure that the joined roles have
+the same word and x.
+
+
+
+
What happens now? Write another select statement to decode this encrypted
+message using the same code table.
CREATE TABLE original AS
+ SELECT 1 AS n, "It's" AS word UNION
+ SELECT 2 , "The" UNION
+ SELECT 3 , "End";
+
+CREATE TABLE code AS
+ SELECT "Up" AS x, "Down" AS y UNION
+ SELECT "Now" , "Home" UNION
+ SELECT "It's" , "What" UNION
+ SELECT "See" , "Do" UNION
+ SELECT "Can" , "See" UNION
+ SELECT "End" , "Now" UNION
+ SELECT "What" , "You" UNION
+ SELECT "The" , "Happens" UNION
+ SELECT "Love" , "Scheme" UNION
+ SELECT "Not" , "Mess" UNION
+ SELECT "Happens", "Go";
+
+SELECT b.y
+ FROM original, code AS a, code AS b
+ WHERE word=a.x AND a.y=b.x
+ ORDER BY n;
+
+
+
+
+
+
+
+
+
+
Join original with code AS a and code AS b to create six-column rows like:
+2|The|The|Happens|Happens|Go, The Go at the end is part of the decoded message.
+
+
+
+
Scheduling time: This is the last discussion, but you could schedule a
+meeting with your group next week to study for the exam. Your regular discussion
+room and time should be available during RRR week if you want to use it.
+
+
+
Document the Occasion
+
+
+
Please all fill out the attendance form (one submission per person per week).
+
+
Important: Please help put the furniture in the room back where you found it before you leave. Thanks!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hw/hw01/hw01.zip b/hw/hw01/hw01.zip
index 65b50b5278..0b97e16c40 100644
Binary files a/hw/hw01/hw01.zip and b/hw/hw01/hw01.zip differ
diff --git a/hw/hw01/ok b/hw/hw01/ok
index b84a800fe7..593354ea21 100644
Binary files a/hw/hw01/ok and b/hw/hw01/ok differ
diff --git a/hw/hw02/hw02.zip b/hw/hw02/hw02.zip
index c07224473c..f6dbfc70b0 100644
Binary files a/hw/hw02/hw02.zip and b/hw/hw02/hw02.zip differ
diff --git a/hw/hw02/ok b/hw/hw02/ok
index b84a800fe7..593354ea21 100644
Binary files a/hw/hw02/ok and b/hw/hw02/ok differ
diff --git a/hw/hw03/hw03.zip b/hw/hw03/hw03.zip
index 6401c69b44..7d2c3ecd67 100644
Binary files a/hw/hw03/hw03.zip and b/hw/hw03/hw03.zip differ
diff --git a/hw/hw03/ok b/hw/hw03/ok
index b84a800fe7..593354ea21 100644
Binary files a/hw/hw03/ok and b/hw/hw03/ok differ
diff --git a/hw/hw04/hw04.zip b/hw/hw04/hw04.zip
index 270c0937c7..43762b390e 100644
Binary files a/hw/hw04/hw04.zip and b/hw/hw04/hw04.zip differ
diff --git a/hw/hw04/ok b/hw/hw04/ok
index b84a800fe7..593354ea21 100644
Binary files a/hw/hw04/ok and b/hw/hw04/ok differ
diff --git a/hw/hw05/construct_check.py b/hw/hw05/construct_check.py
new file mode 100644
index 0000000000..bee096e7e4
--- /dev/null
+++ b/hw/hw05/construct_check.py
@@ -0,0 +1,180 @@
+from ast import parse, NodeVisitor, Name
+
+# For error messages (student-facing) only
+_NAMES = {
+ 'Add': '+',
+ 'And': 'and',
+ 'Assert': 'assert',
+ 'Assign': '=',
+ 'AnnAssign': '=',
+ 'AugAssign': 'op=',
+ 'BitAnd': '&',
+ 'BitOr': '|',
+ 'BitXor': '^',
+ 'Break': 'break',
+ 'Recursion': 'recursive call',
+ 'ClassDef': 'class',
+ 'Continue': 'continue',
+ 'Del': 'del',
+ 'Delete': 'delete',
+ 'Dict': '{...}',
+ 'DictComp': '{...}',
+ 'Div': '/',
+ 'Ellipsis': '...',
+ 'Eq': '==',
+ 'ExceptHandler': 'except',
+ 'ExtSlice': '[::]',
+ 'FloorDiv': '//',
+ 'For': 'for',
+ 'FunctionDef': 'def',
+ 'Filter': 'filter',
+ 'GeneratorExp': '(... for ...)',
+ 'Global': 'global',
+ 'Gt': '>',
+ 'GtE': '>=',
+ 'If': 'if',
+ 'IfExp': '...if...else...',
+ 'Import': 'import',
+ 'ImportFrom': 'from ... import ...',
+ 'In': 'in',
+ 'Index': '...[...]',
+ 'Invert': '~',
+ 'Is': 'is',
+ 'IsNot': 'is not ',
+ 'LShift': '<<',
+ 'Lambda': 'lambda',
+ 'List': '[...]',
+ 'ListComp': '[...for...]',
+ 'Lt': '<',
+ 'LtE': '<=',
+ 'Mod': '%',
+ 'Mult': '*',
+ 'NamedExpr': ':=',
+ 'Nonlocal': 'nonlocal',
+ 'Not': 'not',
+ 'NotEq': '!=',
+ 'NotIn': 'not in',
+ 'Or': 'or',
+ 'Pass': 'pass',
+ 'Pow': '**',
+ 'RShift': '>>',
+ 'Raise': 'raise',
+ 'Return': 'return',
+ 'Set': '{ ... } (set)',
+ 'SetComp': '{ ... for ... } (set)',
+ 'Slice': '[ : ]',
+ 'Starred': '',
+ 'Str': 'str',
+ 'Sub': '-',
+ 'Subscript': '[]',
+ 'Try': 'try',
+ 'Tuple': '(... , ... )',
+ 'UAdd': '+',
+ 'USub': '-',
+ 'While': 'while',
+ 'With': 'with',
+ 'Yield': 'yield',
+ 'YieldFrom': 'yield from',
+}
+
+def check(source_file, checked_funcs, disallow, source=None):
+ """Checks that AST nodes whose type names are present in DISALLOW
+ (an object supporting 'in') are not present in the function(s) named
+ CHECKED_FUNCS in SOURCE. By default, SOURCE is the contents of the
+ file SOURCE_FILE. CHECKED_FUNCS is either a string (indicating a single
+ name) or an object of some other type that supports 'in'. CHECKED_FUNCS
+ may contain __main__ to indicate an entire module. Prints reports of
+ each prohibited node and returns True iff none are found.
+ See ast.__dir__() for AST type names. The special node name 'Recursion'
+ checks for overtly recursive calls (i.e., calls of the form NAME(...) where
+ NAME is an enclosing def."""
+ return ExclusionChecker(disallow).check(source_file, checked_funcs, source)
+
+class ExclusionChecker(NodeVisitor):
+ """An AST visitor that checks that certain constructs are excluded from
+ parts of a program. ExclusionChecker(EXC) checks that AST node types
+ whose names are in the sequence or set EXC are not present. Its check
+ method visits nodes in a given function of a source file checking that the
+ indicated node types are not used."""
+
+ def __init__(self, disallow=()):
+ """DISALLOW is the initial default list of disallowed
+ node-type names."""
+ self._disallow = set(disallow)
+ self._checking = False
+ self._errs = 0
+
+ def generic_visit(self, node):
+ if self._checking and type(node).__name__ in self._disallow:
+ self._report(node)
+ super().generic_visit(node)
+
+ def visit_Module(self, node):
+ if "__main__" in self._checked_funcs:
+ self._checking = True
+ self._checked_name = self._source_file
+ super().generic_visit(node)
+
+ def visit_Call(self, node):
+ if 'Recursion' in self._disallow and \
+ type(node.func) is Name and \
+ node.func.id in self._func_nest:
+ self._report(node, "should not be recursive")
+ self.generic_visit(node)
+
+ def visit_FunctionDef(self, node):
+ self._func_nest.append(node.name)
+ if self._checking:
+ self.generic_visit(node)
+ elif node.name in self._checked_funcs:
+ self._checked_name = "Function " + node.name
+ checking0 = self._checking
+ self._checking = True
+ super().generic_visit(node)
+ self._checking = checking0
+ self._func_nest.pop()
+
+ def _report(self, node, msg=None):
+ node_name = _NAMES.get(type(node).__name__, type(node).__name__)
+ if msg is None:
+ msg = "should not contain '{}'".format(node_name)
+ print("{} {}".format(self._checked_name, msg))
+ self._errs += 1
+
+ def errors(self):
+ """Returns the number of number of prohibited constructs found in
+ the last call to check."""
+ return self._errs
+
+ def check(self, source_file, checked_funcs, disallow=None, source=None):
+ """Checks that AST nodes whose type names are present in DISALLOW
+ (an object supporting the contains test) are not present in
+ the function(s) named CHECKED_FUNCS in SOURCE. By default, SOURCE
+ is the contents of the file SOURCE_FILE. DISALLOW defaults to the
+ argument given to the constructor (and resets that value if it is
+ present). CHECKED_FUNCS is either a string (indicating a single
+ name) or an object of some other type that supports 'in'.
+ CHECKED_FUNCS may contain __main__ to indicate an entire module.
+ Prints reports of each prohibited node and returns True iff none
+ are found.
+ See ast.__dir__() for AST type names. The special node name
+ 'Recursion' checks for overtly recursive calls (i.e., calls of the
+ form NAME(...) where NAME is an enclosing def."""
+
+ self._checking = False
+ self._source_file = source_file
+ self._func_nest = []
+ if type(checked_funcs) is str:
+ self._checked_funcs = { checked_funcs }
+ else:
+ self._checked_funcs = set(checked_funcs)
+ if disallow is not None:
+ self._disallow = set(disallow)
+ if source is None:
+ with open(source_file, 'r', errors='ignore') as inp:
+ source = inp.read()
+ p = parse(source, source_file)
+ self._errs = 0
+
+ self.visit(p)
+ return self._errs == 0
\ No newline at end of file
diff --git a/hw/hw05/hw05.ok b/hw/hw05/hw05.ok
new file mode 100644
index 0000000000..7563437b1c
--- /dev/null
+++ b/hw/hw05/hw05.ok
@@ -0,0 +1,25 @@
+{
+ "name": "Homework 5",
+ "endpoint": "cal/cs61a/fa24/hw05",
+ "src": [
+ "hw05.py"
+ ],
+ "tests": {
+ "hw*.py": "doctest",
+ "tests/*.py": "ok_test"
+ },
+ "default_tests": [
+ "hailstone",
+ "merge",
+ "yield_paths"
+ ],
+ "protocols": [
+ "restore",
+ "file_contents",
+ "analytics",
+ "help",
+ "unlock",
+ "grading",
+ "backup"
+ ]
+}
\ No newline at end of file
diff --git a/hw/hw05/hw05.py b/hw/hw05/hw05.py
new file mode 100644
index 0000000000..02c48e0d06
--- /dev/null
+++ b/hw/hw05/hw05.py
@@ -0,0 +1,135 @@
+def hailstone(n):
+ """Q1: Yields the elements of the hailstone sequence starting at n.
+ At the end of the sequence, yield 1 infinitely.
+
+ >>> hail_gen = hailstone(10)
+ >>> [next(hail_gen) for _ in range(10)]
+ [10, 5, 16, 8, 4, 2, 1, 1, 1, 1]
+ >>> next(hail_gen)
+ 1
+ """
+ "*** YOUR CODE HERE ***"
+
+
+def merge(a, b):
+ """Q2:
+ >>> def sequence(start, step):
+ ... while True:
+ ... yield start
+ ... start += step
+ >>> a = sequence(2, 3) # 2, 5, 8, 11, 14, ...
+ >>> b = sequence(3, 2) # 3, 5, 7, 9, 11, 13, 15, ...
+ >>> result = merge(a, b) # 2, 3, 5, 7, 8, 9, 11, 13, 14, 15
+ >>> [next(result) for _ in range(10)]
+ [2, 3, 5, 7, 8, 9, 11, 13, 14, 15]
+ """
+ "*** YOUR CODE HERE ***"
+
+
+def yield_paths(t, value):
+ """Q4: Yields all possible paths from the root of t to a node with the label
+ value as a list.
+
+ >>> t1 = tree(1, [tree(2, [tree(3), tree(4, [tree(6)]), tree(5)]), tree(5)])
+ >>> print_tree(t1)
+ 1
+ 2
+ 3
+ 4
+ 6
+ 5
+ 5
+ >>> next(yield_paths(t1, 6))
+ [1, 2, 4, 6]
+ >>> path_to_5 = yield_paths(t1, 5)
+ >>> sorted(list(path_to_5))
+ [[1, 2, 5], [1, 5]]
+
+ >>> t2 = tree(0, [tree(2, [t1])])
+ >>> print_tree(t2)
+ 0
+ 2
+ 1
+ 2
+ 3
+ 4
+ 6
+ 5
+ 5
+ >>> path_to_2 = yield_paths(t2, 2)
+ >>> sorted(list(path_to_2))
+ [[0, 2], [0, 2, 1, 2]]
+ """
+ if label(t) == value:
+ yield ____
+ for b in branches(t):
+ for ____ in ____:
+ yield ____
+
+
+
+# Tree Data Abstraction
+
+def tree(label, branches=[]):
+ """Construct a tree with the given label value and a list of branches."""
+ for branch in branches:
+ assert is_tree(branch), 'branches must be trees'
+ return [label] + list(branches)
+
+def label(tree):
+ """Return the label value of a tree."""
+ return tree[0]
+
+def branches(tree):
+ """Return the list of branches of the given tree."""
+ return tree[1:]
+
+def is_tree(tree):
+ """Returns True if the given tree is a tree, and False otherwise."""
+ if type(tree) != list or len(tree) < 1:
+ return False
+ for branch in branches(tree):
+ if not is_tree(branch):
+ return False
+ return True
+
+def is_leaf(tree):
+ """Returns True if the given tree's list of branches is empty, and False
+ otherwise.
+ """
+ return not branches(tree)
+
+def print_tree(t, indent=0):
+ """Print a representation of this tree in which each node is
+ indented by two spaces times its depth from the root.
+
+ >>> print_tree(tree(1))
+ 1
+ >>> print_tree(tree(1, [tree(2)]))
+ 1
+ 2
+ >>> numbers = tree(1, [tree(2), tree(3, [tree(4), tree(5)]), tree(6, [tree(7)])])
+ >>> print_tree(numbers)
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ """
+ print(' ' * indent + str(label(t)))
+ for b in branches(t):
+ print_tree(b, indent + 1)
+
+def copy_tree(t):
+ """Returns a copy of t. Only for testing purposes.
+
+ >>> t = tree(5)
+ >>> copy = copy_tree(t)
+ >>> t = tree(6)
+ >>> print_tree(copy)
+ 5
+ """
+ return tree(label(t), [copy_tree(b) for b in branches(t)])
+
diff --git a/hw/hw05/hw05.zip b/hw/hw05/hw05.zip
new file mode 100644
index 0000000000..d9f6b60540
Binary files /dev/null and b/hw/hw05/hw05.zip differ
diff --git a/hw/hw05/index.html b/hw/hw05/index.html
new file mode 100644
index 0000000000..f3bf96302b
--- /dev/null
+++ b/hw/hw05/index.html
@@ -0,0 +1,538 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Homework 5 | CS 61A Fall 2024
+
+
+
+
+
+
+
+
+
Download hw05.zip. Inside the archive, you will find a file called
+hw05.py, along with a copy of the ok autograder.
+
+
Submission: When you are done, submit the assignment by uploading all code files you've edited to Gradescope. You may submit more than once before the deadline; only the
+final submission will be scored. Check that you have successfully submitted
+your code on Gradescope. See Lab 0 for more instructions on
+submitting assignments.
+
+
Using Ok: If you have any questions about using Ok, please
+refer to this guide.
+
+
+
Readings: You might find the following references
+ useful:
Grading: Homework is graded based on
+correctness. Each incorrect problem will decrease the total score by one point.
+This homework is out of 2 points.
+
+
+
+
+
+
Required Questions
+
+
+
+
+
+
+
+
+
Getting Started Videos
+
+
+
These videos may provide some helpful direction for tackling the coding
+problems on this assignment.
+
+
To see these videos, you should be logged into your berkeley.edu email.
Write a generator function that yields the elements of the hailstone sequence starting at number n.
+After reaching the end of the hailstone sequence, the generator should yield the value 1 indefinitely.
+
+
Here's a quick reminder of how the hailstone sequence is defined:
+
+
+
Pick a positive integer n as the start.
+
If n is even, divide it by 2.
+
If n is odd, multiply it by 3 and add 1.
+
Continue this process until n is 1.
+
+
+
Try to write this generator function recursively. If you're stuck, you can first try writing it iteratively
+and then seeing how you can turn that implementation into a recursive one.
+
+
Hint: Since hailstone returns a generator, you can yield from a call to hailstone!
+
+
+
+
def hailstone(n):
+ """Q1: Yields the elements of the hailstone sequence starting at n.
+ At the end of the sequence, yield 1 infinitely.
+
+ >>> hail_gen = hailstone(10)
+ >>> [next(hail_gen) for _ in range(10)]
+ [10, 5, 16, 8, 4, 2, 1, 1, 1, 1]
+ >>> next(hail_gen)
+ 1
+ """
+ "*** YOUR CODE HERE ***"
+
+
+
+
+
Use Ok to test your code:
python3 ok -q hailstone
+
+
+
+
+
Q2: Merge
+
+
Write a generator function merge that takes in two infinite generators a and b that are in increasing order without duplicates and returns a generator that has all the elements of both generators, in increasing order, without duplicates.
Define a generator function yield_paths which takes in a tree t, a value
+value, and returns a generator object which yields each path from the root
+of t to a node that has label value.
+
+
Each path should be represented as a list of the labels along that path in the
+tree. You may yield the paths in any order.
+
+
def yield_paths(t, value):
+ """Q4: Yields all possible paths from the root of t to a node with the label
+ value as a list.
+
+ >>> t1 = tree(1, [tree(2, [tree(3), tree(4, [tree(6)]), tree(5)]), tree(5)])
+ >>> print_tree(t1)
+ 1
+ 2
+ 3
+ 4
+ 6
+ 5
+ 5
+ >>> next(yield_paths(t1, 6))
+ [1, 2, 4, 6]
+ >>> path_to_5 = yield_paths(t1, 5)
+ >>> sorted(list(path_to_5))
+ [[1, 2, 5], [1, 5]]
+
+ >>> t2 = tree(0, [tree(2, [t1])])
+ >>> print_tree(t2)
+ 0
+ 2
+ 1
+ 2
+ 3
+ 4
+ 6
+ 5
+ 5
+ >>> path_to_2 = yield_paths(t2, 2)
+ >>> sorted(list(path_to_2))
+ [[0, 2], [0, 2, 1, 2]]
+ """
+ if label(t) == value:
+ yield ____
+ for b in branches(t):
+ for ____ in ____:
+ yield ____
+
+
+
Hint: If you're having trouble getting started, think about how you'd approach this
+problem if it wasn't a generator function. What would your recursive calls be?
+With a generator function, what happens if you make a "recursive call" within its body?
+
+
+
+
Hint: Try coming up with a few simple cases of your own! How should this function work when t is a leaf node?
+
+
+
+
Hint: Remember, it's possible to loop over generator objects because generators are iterators!
+
+
+
+
Note: Remember that this problem should yield paths -- do not return a list of paths!
+
+
+
+
Use Ok to test your code:
python3 ok -q yield_paths
+
+
+
+
+
+
+
Check Your Score Locally
+
+
You can locally check your score on each question of this assignment by running
+
+
python3 ok --score
+
+
This does NOT submit the assignment! When you are satisfied with your score, submit the assignment to Gradescope to receive credit for it.
+
+
+
Submit Assignment
+
+
+
Submit this assignment by uploading any files you've edited to the appropriate Gradescope assignment.Lab 00 has detailed instructions.
+
+
+
Exam Practice
+
+
+
Homework assignments will also contain prior exam questions for you to try.
+These questions have no submission component; feel free to attempt them if you'd like some practice!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hw/hw05/ok b/hw/hw05/ok
new file mode 100644
index 0000000000..593354ea21
Binary files /dev/null and b/hw/hw05/ok differ
diff --git a/hw/hw06/construct_check.py b/hw/hw06/construct_check.py
new file mode 100644
index 0000000000..bee096e7e4
--- /dev/null
+++ b/hw/hw06/construct_check.py
@@ -0,0 +1,180 @@
+from ast import parse, NodeVisitor, Name
+
+# For error messages (student-facing) only
+_NAMES = {
+ 'Add': '+',
+ 'And': 'and',
+ 'Assert': 'assert',
+ 'Assign': '=',
+ 'AnnAssign': '=',
+ 'AugAssign': 'op=',
+ 'BitAnd': '&',
+ 'BitOr': '|',
+ 'BitXor': '^',
+ 'Break': 'break',
+ 'Recursion': 'recursive call',
+ 'ClassDef': 'class',
+ 'Continue': 'continue',
+ 'Del': 'del',
+ 'Delete': 'delete',
+ 'Dict': '{...}',
+ 'DictComp': '{...}',
+ 'Div': '/',
+ 'Ellipsis': '...',
+ 'Eq': '==',
+ 'ExceptHandler': 'except',
+ 'ExtSlice': '[::]',
+ 'FloorDiv': '//',
+ 'For': 'for',
+ 'FunctionDef': 'def',
+ 'Filter': 'filter',
+ 'GeneratorExp': '(... for ...)',
+ 'Global': 'global',
+ 'Gt': '>',
+ 'GtE': '>=',
+ 'If': 'if',
+ 'IfExp': '...if...else...',
+ 'Import': 'import',
+ 'ImportFrom': 'from ... import ...',
+ 'In': 'in',
+ 'Index': '...[...]',
+ 'Invert': '~',
+ 'Is': 'is',
+ 'IsNot': 'is not ',
+ 'LShift': '<<',
+ 'Lambda': 'lambda',
+ 'List': '[...]',
+ 'ListComp': '[...for...]',
+ 'Lt': '<',
+ 'LtE': '<=',
+ 'Mod': '%',
+ 'Mult': '*',
+ 'NamedExpr': ':=',
+ 'Nonlocal': 'nonlocal',
+ 'Not': 'not',
+ 'NotEq': '!=',
+ 'NotIn': 'not in',
+ 'Or': 'or',
+ 'Pass': 'pass',
+ 'Pow': '**',
+ 'RShift': '>>',
+ 'Raise': 'raise',
+ 'Return': 'return',
+ 'Set': '{ ... } (set)',
+ 'SetComp': '{ ... for ... } (set)',
+ 'Slice': '[ : ]',
+ 'Starred': '',
+ 'Str': 'str',
+ 'Sub': '-',
+ 'Subscript': '[]',
+ 'Try': 'try',
+ 'Tuple': '(... , ... )',
+ 'UAdd': '+',
+ 'USub': '-',
+ 'While': 'while',
+ 'With': 'with',
+ 'Yield': 'yield',
+ 'YieldFrom': 'yield from',
+}
+
+def check(source_file, checked_funcs, disallow, source=None):
+ """Checks that AST nodes whose type names are present in DISALLOW
+ (an object supporting 'in') are not present in the function(s) named
+ CHECKED_FUNCS in SOURCE. By default, SOURCE is the contents of the
+ file SOURCE_FILE. CHECKED_FUNCS is either a string (indicating a single
+ name) or an object of some other type that supports 'in'. CHECKED_FUNCS
+ may contain __main__ to indicate an entire module. Prints reports of
+ each prohibited node and returns True iff none are found.
+ See ast.__dir__() for AST type names. The special node name 'Recursion'
+ checks for overtly recursive calls (i.e., calls of the form NAME(...) where
+ NAME is an enclosing def."""
+ return ExclusionChecker(disallow).check(source_file, checked_funcs, source)
+
+class ExclusionChecker(NodeVisitor):
+ """An AST visitor that checks that certain constructs are excluded from
+ parts of a program. ExclusionChecker(EXC) checks that AST node types
+ whose names are in the sequence or set EXC are not present. Its check
+ method visits nodes in a given function of a source file checking that the
+ indicated node types are not used."""
+
+ def __init__(self, disallow=()):
+ """DISALLOW is the initial default list of disallowed
+ node-type names."""
+ self._disallow = set(disallow)
+ self._checking = False
+ self._errs = 0
+
+ def generic_visit(self, node):
+ if self._checking and type(node).__name__ in self._disallow:
+ self._report(node)
+ super().generic_visit(node)
+
+ def visit_Module(self, node):
+ if "__main__" in self._checked_funcs:
+ self._checking = True
+ self._checked_name = self._source_file
+ super().generic_visit(node)
+
+ def visit_Call(self, node):
+ if 'Recursion' in self._disallow and \
+ type(node.func) is Name and \
+ node.func.id in self._func_nest:
+ self._report(node, "should not be recursive")
+ self.generic_visit(node)
+
+ def visit_FunctionDef(self, node):
+ self._func_nest.append(node.name)
+ if self._checking:
+ self.generic_visit(node)
+ elif node.name in self._checked_funcs:
+ self._checked_name = "Function " + node.name
+ checking0 = self._checking
+ self._checking = True
+ super().generic_visit(node)
+ self._checking = checking0
+ self._func_nest.pop()
+
+ def _report(self, node, msg=None):
+ node_name = _NAMES.get(type(node).__name__, type(node).__name__)
+ if msg is None:
+ msg = "should not contain '{}'".format(node_name)
+ print("{} {}".format(self._checked_name, msg))
+ self._errs += 1
+
+ def errors(self):
+ """Returns the number of number of prohibited constructs found in
+ the last call to check."""
+ return self._errs
+
+ def check(self, source_file, checked_funcs, disallow=None, source=None):
+ """Checks that AST nodes whose type names are present in DISALLOW
+ (an object supporting the contains test) are not present in
+ the function(s) named CHECKED_FUNCS in SOURCE. By default, SOURCE
+ is the contents of the file SOURCE_FILE. DISALLOW defaults to the
+ argument given to the constructor (and resets that value if it is
+ present). CHECKED_FUNCS is either a string (indicating a single
+ name) or an object of some other type that supports 'in'.
+ CHECKED_FUNCS may contain __main__ to indicate an entire module.
+ Prints reports of each prohibited node and returns True iff none
+ are found.
+ See ast.__dir__() for AST type names. The special node name
+ 'Recursion' checks for overtly recursive calls (i.e., calls of the
+ form NAME(...) where NAME is an enclosing def."""
+
+ self._checking = False
+ self._source_file = source_file
+ self._func_nest = []
+ if type(checked_funcs) is str:
+ self._checked_funcs = { checked_funcs }
+ else:
+ self._checked_funcs = set(checked_funcs)
+ if disallow is not None:
+ self._disallow = set(disallow)
+ if source is None:
+ with open(source_file, 'r', errors='ignore') as inp:
+ source = inp.read()
+ p = parse(source, source_file)
+ self._errs = 0
+
+ self.visit(p)
+ return self._errs == 0
\ No newline at end of file
diff --git a/hw/hw06/hw06.ok b/hw/hw06/hw06.ok
new file mode 100644
index 0000000000..6c88d9378f
--- /dev/null
+++ b/hw/hw06/hw06.ok
@@ -0,0 +1,24 @@
+{
+ "name": "Homework 6",
+ "endpoint": "cal/cs61a/fa24/hw06",
+ "src": [
+ "hw06.py"
+ ],
+ "tests": {
+ "hw*.py": "doctest",
+ "tests/*.py": "ok_test"
+ },
+ "default_tests": [
+ "VendingMachine",
+ "midsem_survey"
+ ],
+ "protocols": [
+ "restore",
+ "file_contents",
+ "help",
+ "grading",
+ "analytics",
+ "unlock",
+ "backup"
+ ]
+}
\ No newline at end of file
diff --git a/hw/hw06/hw06.py b/hw/hw06/hw06.py
new file mode 100644
index 0000000000..036e01e76c
--- /dev/null
+++ b/hw/hw06/hw06.py
@@ -0,0 +1,156 @@
+passphrase = 'REPLACE_THIS_WITH_PASSPHRASE'
+
+def midsem_survey(p):
+ """
+ You do not need to understand this code.
+ >>> midsem_survey(passphrase)
+ 'c20d4e5854c4c9cdfd14626aad39bd5a1e2ed9bb30dca56d5643a3ad'
+ """
+ import hashlib
+ return hashlib.sha224(p.encode('utf-8')).hexdigest()
+
+
+class VendingMachine:
+ """A vending machine that vends some product for some price.
+
+ >>> v = VendingMachine('candy', 10)
+ >>> v.vend()
+ 'Nothing left to vend. Please restock.'
+ >>> v.add_funds(15)
+ 'Nothing left to vend. Please restock. Here is your $15.'
+ >>> v.restock(2)
+ 'Current candy stock: 2'
+ >>> v.vend()
+ 'Please add $10 more funds.'
+ >>> v.add_funds(7)
+ 'Current balance: $7'
+ >>> v.vend()
+ 'Please add $3 more funds.'
+ >>> v.add_funds(5)
+ 'Current balance: $12'
+ >>> v.vend()
+ 'Here is your candy and $2 change.'
+ >>> v.add_funds(10)
+ 'Current balance: $10'
+ >>> v.vend()
+ 'Here is your candy.'
+ >>> v.add_funds(15)
+ 'Nothing left to vend. Please restock. Here is your $15.'
+
+ >>> w = VendingMachine('soda', 2)
+ >>> w.restock(3)
+ 'Current soda stock: 3'
+ >>> w.restock(3)
+ 'Current soda stock: 6'
+ >>> w.add_funds(2)
+ 'Current balance: $2'
+ >>> w.vend()
+ 'Here is your soda.'
+ """
+ "*** YOUR CODE HERE ***"
+
+
+def store_digits(n):
+ """Stores the digits of a positive number n in a linked list.
+
+ >>> s = store_digits(1)
+ >>> s
+ Link(1)
+ >>> store_digits(2345)
+ Link(2, Link(3, Link(4, Link(5))))
+ >>> store_digits(876)
+ Link(8, Link(7, Link(6)))
+ >>> store_digits(2450)
+ Link(2, Link(4, Link(5, Link(0))))
+ >>> # a check for restricted functions
+ >>> import inspect, re
+ >>> cleaned = re.sub(r"#.*\\n", '', re.sub(r'"{3}[\s\S]*?"{3}', '', inspect.getsource(store_digits)))
+ >>> print("Do not use str or reversed!") if any([r in cleaned for r in ["str", "reversed"]]) else None
+ """
+ "*** YOUR CODE HERE ***"
+
+
+def deep_map_mut(func, lnk):
+ """Mutates a deep link lnk by replacing each item found with the
+ result of calling func on the item. Does NOT create new Links (so
+ no use of Link's constructor).
+
+ Does not return the modified Link object.
+
+ >>> link1 = Link(3, Link(Link(4), Link(5, Link(6))))
+ >>> print(link1)
+ <3 <4> 5 6>
+ >>> # Disallow the use of making new Links before calling deep_map_mut
+ >>> Link.__init__, hold = lambda *args: print("Do not create any new Links."), Link.__init__
+ >>> try:
+ ... deep_map_mut(lambda x: x * x, link1)
+ ... finally:
+ ... Link.__init__ = hold
+ >>> print(link1)
+ <9 <16> 25 36>
+ """
+ "*** YOUR CODE HERE ***"
+
+
+def two_list(vals, counts):
+ """
+ Returns a linked list according to the two lists that were passed in. Assume
+ vals and counts are the same size. Elements in vals represent the value, and the
+ corresponding element in counts represents the number of this value desired in the
+ final linked list. Assume all elements in counts are greater than 0. Assume both
+ lists have at least one element.
+ >>> a = [1, 3]
+ >>> b = [1, 1]
+ >>> c = two_list(a, b)
+ >>> c
+ Link(1, Link(3))
+ >>> a = [1, 3, 2]
+ >>> b = [2, 2, 1]
+ >>> c = two_list(a, b)
+ >>> c
+ Link(1, Link(1, Link(3, Link(3, Link(2)))))
+ """
+ "*** YOUR CODE HERE ***"
+
+
+class Link:
+ """A linked list.
+
+ >>> s = Link(1)
+ >>> s.first
+ 1
+ >>> s.rest is Link.empty
+ True
+ >>> s = Link(2, Link(3, Link(4)))
+ >>> s.first = 5
+ >>> s.rest.first = 6
+ >>> s.rest.rest = Link.empty
+ >>> s # Displays the contents of repr(s)
+ Link(5, Link(6))
+ >>> s.rest = Link(7, Link(Link(8, Link(9))))
+ >>> s
+ Link(5, Link(7, Link(Link(8, Link(9)))))
+ >>> print(s) # Prints str(s)
+ <5 7 <8 9>>
+ """
+ empty = ()
+
+ def __init__(self, first, rest=empty):
+ assert rest is Link.empty or isinstance(rest, Link)
+ self.first = first
+ self.rest = rest
+
+ def __repr__(self):
+ if self.rest is not Link.empty:
+ rest_repr = ', ' + repr(self.rest)
+ else:
+ rest_repr = ''
+ return 'Link(' + repr(self.first) + rest_repr + ')'
+
+ def __str__(self):
+ string = '<'
+ while self.rest is not Link.empty:
+ string += str(self.first) + ' '
+ self = self.rest
+ return string + str(self.first) + '>'
+
diff --git a/hw/hw06/hw06.zip b/hw/hw06/hw06.zip
new file mode 100644
index 0000000000..5821c93a57
Binary files /dev/null and b/hw/hw06/hw06.zip differ
diff --git a/hw/hw06/index.html b/hw/hw06/index.html
new file mode 100644
index 0000000000..316aa54e55
--- /dev/null
+++ b/hw/hw06/index.html
@@ -0,0 +1,629 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Homework 6 | CS 61A Fall 2024
+
+
+
+
+
+
+
+
+
Download hw06.zip. Inside the archive, you will find a file called
+hw06.py, along with a copy of the ok autograder.
+
+
Submission: When you are done, submit the assignment by uploading all code files you've edited to Gradescope. You may submit more than once before the deadline; only the
+final submission will be scored. Check that you have successfully submitted
+your code on Gradescope. See Lab 0 for more instructions on
+submitting assignments.
+
+
Using Ok: If you have any questions about using Ok, please
+refer to this guide.
+
+
+
+
Grading: Homework is graded based on
+correctness. Each incorrect problem will decrease the total score by one point.
+This homework is out of 2 points.
+
+
+
+
+
+
Required Questions
+
+
+
+
+
+
+
+
Getting Started Videos
+
+
+
These videos may provide some helpful direction for tackling the coding
+problems on this assignment.
+
+
To see these videos, you should be logged into your berkeley.edu email.
Confidentiality:
+Your responses to the survey are confidential, and only the instructors
+will be able to see this data unanonymized.
+More specifics on confidentiality can be found on the survey itself.
+
+
Once you finish the survey, you will be presented with a passphrase (if you miss
+it, it should also be at the bottom of the confirmation email you receive). Put
+this passphrase, as a string, on the line that says
+passphrase = 'REPLACE_THIS_WITH_PASSPHRASE' in the Python file for this assignment.
+E.g. if the passphrase is abc, then the line should be passphrase = 'abc'.
+
+
Use Ok to test your code:
python3 ok -q midsem_survey
+
+
+
+
+
OOP
+
+
+
Q2: Vending Machine
+
+
+
In this question you'll create a vending machine that sells a single product and provides change when needed.
+
+
Create a class called VendingMachine that represents a vending
+machine for some product. A VendingMachine object returns strings
+describing its interactions. Make sure your output exactly matches the strings in the doctests including punctuation and spacing!
+
+
You may find Python's formatted string literals, or f-strings useful.
+A quick example:
Fill in the VendingMachine class, adding attributes and methods as appropriate, such that its behavior matches the following doctests:
+
+
+
+
class VendingMachine:
+ """A vending machine that vends some product for some price.
+
+ >>> v = VendingMachine('candy', 10)
+ >>> v.vend()
+ 'Nothing left to vend. Please restock.'
+ >>> v.add_funds(15)
+ 'Nothing left to vend. Please restock. Here is your $15.'
+ >>> v.restock(2)
+ 'Current candy stock: 2'
+ >>> v.vend()
+ 'Please add $10 more funds.'
+ >>> v.add_funds(7)
+ 'Current balance: $7'
+ >>> v.vend()
+ 'Please add $3 more funds.'
+ >>> v.add_funds(5)
+ 'Current balance: $12'
+ >>> v.vend()
+ 'Here is your candy and $2 change.'
+ >>> v.add_funds(10)
+ 'Current balance: $10'
+ >>> v.vend()
+ 'Here is your candy.'
+ >>> v.add_funds(15)
+ 'Nothing left to vend. Please restock. Here is your $15.'
+
+ >>> w = VendingMachine('soda', 2)
+ >>> w.restock(3)
+ 'Current soda stock: 3'
+ >>> w.restock(3)
+ 'Current soda stock: 6'
+ >>> w.add_funds(2)
+ 'Current balance: $2'
+ >>> w.vend()
+ 'Here is your soda.'
+ """
+ "*** YOUR CODE HERE ***"
+
+
+
+
+
Use Ok to test your code:
python3 ok -q VendingMachine
+
+
+
+
+
+
+
Check Your Score Locally
+
+
You can locally check your score on each question of this assignment by running
+
+
python3 ok --score
+
+
This does NOT submit the assignment! When you are satisfied with your score, submit the assignment to Gradescope to receive credit for it.
+
+
Submit Assignment
+
+
+
Submit this assignment by uploading any files you've edited to the appropriate Gradescope assignment.Lab 00 has detailed instructions.
+
+
+
Optional Questions
+
+
+
Q3: Store Digits
+
+
+
Write a function store_digits that takes in an integer n and returns
+a linked list where each element of the list is a digit of n.
+
+
Important: Do not use any string manipulation functions like str and reversed.
+
+
+
+
def store_digits(n):
+ """Stores the digits of a positive number n in a linked list.
+
+ >>> s = store_digits(1)
+ >>> s
+ Link(1)
+ >>> store_digits(2345)
+ Link(2, Link(3, Link(4, Link(5))))
+ >>> store_digits(876)
+ Link(8, Link(7, Link(6)))
+ >>> store_digits(2450)
+ Link(2, Link(4, Link(5, Link(0))))
+ >>> # a check for restricted functions
+ >>> import inspect, re
+ >>> cleaned = re.sub(r"#.*\\n", '', re.sub(r'"{3}[\s\S]*?"{3}', '', inspect.getsource(store_digits)))
+ >>> print("Do not use str or reversed!") if any([r in cleaned for r in ["str", "reversed"]]) else None
+ """
+ "*** YOUR CODE HERE ***"
+
+
+
+
+
Use Ok to test your code:
python3 ok -q store_digits
+
+
+
+
+
Q4: Mutable Mapping
+
+
+
Implement deep_map_mut(func, link), which applies a function func onto
+all elements in the given linked list lnk. If an element is itself a
+linked list, apply func to each of its elements, and so on.
+
+
Your implementation should mutate the original linked list. Do not
+create any new linked lists.
+
+
Hint: The built-in isinstance function may be useful.
Construct Check: The last doctest of this question ensures that you
+do not create new linked lists. If you are failing this doctest, ensure
+that you are not creating link lists by calling the constructor, i.e.
+
+
s = Link(1)
+
+
+
+
def deep_map_mut(func, lnk):
+ """Mutates a deep link lnk by replacing each item found with the
+ result of calling func on the item. Does NOT create new Links (so
+ no use of Link's constructor).
+
+ Does not return the modified Link object.
+
+ >>> link1 = Link(3, Link(Link(4), Link(5, Link(6))))
+ >>> print(link1)
+ <3 <4> 5 6>
+ >>> # Disallow the use of making new Links before calling deep_map_mut
+ >>> Link.__init__, hold = lambda *args: print("Do not create any new Links."), Link.__init__
+ >>> try:
+ ... deep_map_mut(lambda x: x * x, link1)
+ ... finally:
+ ... Link.__init__ = hold
+ >>> print(link1)
+ <9 <16> 25 36>
+ """
+ "*** YOUR CODE HERE ***"
+
+
+
+
+
Use Ok to test your code:
python3 ok -q deep_map_mut
+
+
+
+
+
Q5: Two List
+
+
+
Implement a function two_list that takes in two lists and returns a linked list. The first list contains the
+values that we want to put in the linked list, and the second list contains the number of each corresponding value.
+Assume both lists are the same size and have a length of 1 or greater. Assume all elements in the second list
+are greater than 0.
+
+
+
+
def two_list(vals, counts):
+ """
+ Returns a linked list according to the two lists that were passed in. Assume
+ vals and counts are the same size. Elements in vals represent the value, and the
+ corresponding element in counts represents the number of this value desired in the
+ final linked list. Assume all elements in counts are greater than 0. Assume both
+ lists have at least one element.
+ >>> a = [1, 3]
+ >>> b = [1, 1]
+ >>> c = two_list(a, b)
+ >>> c
+ Link(1, Link(3))
+ >>> a = [1, 3, 2]
+ >>> b = [2, 2, 1]
+ >>> c = two_list(a, b)
+ >>> c
+ Link(1, Link(1, Link(3, Link(3, Link(2)))))
+ """
+ "*** YOUR CODE HERE ***"
+
+
+
+
+
Use Ok to test your code:
python3 ok -q two_list
+
+
+
+
+
Exam Practice
+
+
Homework assignments will also contain prior exam questions for you to try.
+These questions have no submission component; feel free to attempt them if you'd like some practice!
Download hw07.zip. Inside the archive, you will find a file called
+hw07.scm, along with a copy of the ok autograder.
+
+
Submission: When you are done, submit the assignment by uploading all code files you've edited to Gradescope. You may submit more than once before the deadline; only the
+final submission will be scored. Check that you have successfully submitted
+your code on Gradescope. See Lab 0 for more instructions on
+submitting assignments.
+
+
Using Ok: If you have any questions about using Ok, please
+refer to this guide.
+
+
+
Readings: You might find the following references
+ useful:
Grading: Homework is graded based on
+correctness. Each incorrect problem will decrease the total score by one point.
+This homework is out of 2 points.
+
+
+
+
+
+
The 61A Scheme interpreter is included in each Scheme assignment. To start it,
+type python3 scheme in a terminal. To load a Scheme file called f.scm, type python3 scheme -i f.scm. To exit the Scheme interpreter, type
+(exit).
+
+
Scheme Editor
+
+
+
All Scheme assignments include a web-based editor that makes it easy to run ok
+tests and visualize environments. Type python3 editor in a terminal, and the
+editor will open in a browser window (at http://127.0.0.1:31415/). Whatever
+changes you make here will also save to the original file on your computer!
+To stop running the editor and return to the command line, type Ctrl-C in the
+terminal where you started the editor.
+
+
The Run button loads the current assignment's .scm file and opens a Scheme
+interpreter, allowing you to try evaluating different Scheme expressions.
+
+
The Test button runs all ok tests for the assignment. Click View Case for a
+failed test, then click Debug to step through its evaluation.
+
+
Recommended VS Code Extensions
+
+
+
If you choose to use VS Code as your text editor (instead of the web-based editor),
+install the vscode-scheme
+extension so that parentheses are highlighted.
+
+
Before:
+
+
+
+
After:
+
+
+
+
In addition, the 61a-bot (installation instructions) VS Code extension is available for Scheme homeworks. The bot is also integrated into ok.
+
+
+
Required Questions
+
+
+
+
+
+
+
+
Getting Started Videos
+
+
+
These videos may provide some helpful direction for tackling the coding
+problems on this assignment.
+
+
To see these videos, you should be logged into your berkeley.edu email.
Implement a procedure pow that raises a base to the power of a nonnegative integer exp. The number of recursive pow calls should grow logarithmically with respect to exp, rather than linearly. For example, (pow 2 32) should result in 5 recursive pow calls rather than 32 recursive pow calls.
+
+
Hint:
+
+
+
x2y = (xy)2
+
x2y+1 = x(xy)2
+
+
+
For example, 216 = (28)2 and 217 = 2 * (28)2.
+
+
You may use the built-in predicates even? and odd?. Also, the square procedure is defined for you.
+
+
Scheme doesn't have while or for statements, so use recursion to solve this problem.
+
+
+
+
(define (square n) (* n n))
+
+(define (pow base exp)
+ 'YOUR-CODE-HERE
+)
+
+
+
+
+
Use Ok to test your code:
python3 ok -q pow
+
+
+
+
+
+
+
Q2: Repeatedly Cube
+
+
+
Implement repeatedly-cube, which receives a number x and cubes it n times.
+
+
Here are some examples of how repeatedly-cube should behave:
+
+
scm> (repeatedly-cube 100 1) ; 1 cubed 100 times is still 1
+1
+scm> (repeatedly-cube 2 2) ; (2^3)^3
+512
+scm> (repeatedly-cube 3 2) ; ((2^3)^3)^3
+134217728
Download hw08.zip. Inside the archive, you will find a file called
+hw08.scm, along with a copy of the ok autograder.
+
+
Submission: When you are done, submit the assignment by uploading all code files you've edited to Gradescope. You may submit more than once before the deadline; only the
+final submission will be scored. Check that you have successfully submitted
+your code on Gradescope. See Lab 0 for more instructions on
+submitting assignments.
+
+
Using Ok: If you have any questions about using Ok, please
+refer to this guide.
+
+
+
Readings: You might find the following references
+ useful:
Grading: Homework is graded based on
+correctness. Each incorrect problem will decrease the total score by one point.
+This homework is out of 2 points.
+
+
+
+
+
+
The 61A Scheme interpreter is included in each Scheme assignment. To start it,
+type python3 scheme in a terminal. To load a Scheme file called f.scm, type python3 scheme -i f.scm. To exit the Scheme interpreter, type
+(exit).
+
+
Scheme Editor
+
+
+
All Scheme assignments include a web-based editor that makes it easy to run ok
+tests and visualize environments. Type python3 editor in a terminal, and the
+editor will open in a browser window (at http://127.0.0.1:31415/). Whatever
+changes you make here will also save to the original file on your computer!
+To stop running the editor and return to the command line, type Ctrl-C in the
+terminal where you started the editor.
+
+
The Run button loads the current assignment's .scm file and opens a Scheme
+interpreter, allowing you to try evaluating different Scheme expressions.
+
+
The Test button runs all ok tests for the assignment. Click View Case for a
+failed test, then click Debug to step through its evaluation.
+
+
Recommended VS Code Extensions
+
+
+
If you choose to use VS Code as your text editor (instead of the web-based editor),
+install the vscode-scheme
+extension so that parentheses are highlighted.
+
+
Before:
+
+
+
+
After:
+
+
+
+
In addition, the 61a-bot (installation instructions) VS Code extension is available for Scheme homeworks. The bot is also integrated into ok.
+
+
+
Required Questions
+
+
+
+
Required Questions
+
+
+
+
+
+
+
+
Getting Started Videos
+
+
+
These videos may provide some helpful direction for tackling the coding
+problems on this assignment.
+
+
To see these videos, you should be logged into your berkeley.edu email.
Implement a procedure called ascending?, which takes a list of numbers s and
+returns True if the numbers are in non-descending order, and False
+otherwise.
+
+
A list of numbers is non-descending if each element after the first is
+greater than or equal to the previous element. For example...
+
+
+
(1 2 3 3 4) is non-descending.
+
(1 2 3 3 2) is not.
+
+
+
Hint: The built-in null? procedure returns whether its argument is nil.
+
+
+
+
Note: The question mark in ascending? is just part of the procedure name and has no special meaning in terms of Scheme syntax. It is a common practice in Scheme to name procedures with a question mark at the end if it returns a boolean value.
+
+
(define (ascending? s)
+ 'YOUR-CODE-HERE
+)
+
+
+
+
+
+
Use Ok to unlock and test your code:
python3 ok -q ascending -u
+python3 ok -q ascending
+
+
+
+
+
+
Q2: My Filter
+
+
+
Write a procedure my-filter, which takes a predicate pred and a list s, and
+returns a new list containing only elements of the list that satisfy the
+predicate. The output should contain the elements in the same order that they
+appeared in the original list.
+
+
Note: Make sure that you are not just calling the built-in filter function in Scheme - we are asking you to re-implement this!
+
+
+
+
(define (my-filter pred s)
+ 'YOUR-CODE-HERE
+)
+
+
+
+
+
+
+
Use Ok to unlock and test your code:
python3 ok -q filter -u
+python3 ok -q filter
+
+
+
+
+
+
Q3: Interleave
+
+
+
Implement the function interleave, which takes two lists lst1 and lst2 as
+arguments. interleave should return a list that interleaves the elements
+of the two lists. (In other words, the resulting list should contain elements
+alternating between lst1 and lst2, starting at lst1).
+
+
If one of the input lists to interleave is shorter than the other, then
+interleave should alternate elements from both lists until one list has no
+more elements, and then the remaining elements from the longer list should be
+added to the end of the new list. If lst1 is empty, you may simply return
+lst2 and vice versa.
python3 ok -q interleave -u
+python3 ok -q interleave
+
+
+
+
+
Q4: No Repeats
+
+
+
Implement no-repeats, which takes a list of numbers s. It returns a list
+that has all of the unique elements of s in the order that they first appear,
+but no repeats.
+
+
For example, (no-repeats (list 5 4 5 4 2 2)) evaluates to (5 4 2).
+
+
Hint: You may find it helpful to use filter with a lambda procedure to
+filter out repeats. To test if two numbers a and b are not equal, use
+(not (= a b)).
+
+
+
+
(define (no-repeats s)
+ 'YOUR-CODE-HERE
+)
+
+
+
+
+
+
+
Use Ok to test your code:
python3 ok -q no_repeats
+
+
+
+
+
Submit Assignment
+
+
+
Submit this assignment by uploading any files you've edited to the appropriate Gradescope assignment.Lab 00 has detailed instructions.
+
+
+
Exam Practice
+
+
The following are some Scheme List exam problems from previous semesters that you may find useful as additional exam practice.
Download hw09.zip. Inside the archive, you will find a file called
+hw09.scm, along with a copy of the ok autograder.
+
+
Submission: When you are done, submit the assignment by uploading all code files you've edited to Gradescope. You may submit more than once before the deadline; only the
+final submission will be scored. Check that you have successfully submitted
+your code on Gradescope. See Lab 0 for more instructions on
+submitting assignments.
+
+
Using Ok: If you have any questions about using Ok, please
+refer to this guide.
+
+
+
Readings: You might find the following references
+ useful:
Grading: Homework is graded based on
+correctness. Each incorrect problem will decrease the total score by one point.
+This homework is out of 2 points.
+
+
+
+
+
+
The 61A Scheme interpreter is included in each Scheme assignment. To start it,
+type python3 scheme in a terminal. To load a Scheme file called f.scm, type python3 scheme -i f.scm. To exit the Scheme interpreter, type
+(exit).
+
+
Scheme Editor
+
+
+
All Scheme assignments include a web-based editor that makes it easy to run ok
+tests and visualize environments. Type python3 editor in a terminal, and the
+editor will open in a browser window (at http://127.0.0.1:31415/). Whatever
+changes you make here will also save to the original file on your computer!
+To stop running the editor and return to the command line, type Ctrl-C in the
+terminal where you started the editor.
+
+
The Run button loads the current assignment's .scm file and opens a Scheme
+interpreter, allowing you to try evaluating different Scheme expressions.
+
+
The Test button runs all ok tests for the assignment. Click View Case for a
+failed test, then click Debug to step through its evaluation.
+
+
Recommended VS Code Extensions
+
+
+
If you choose to use VS Code as your text editor (instead of the web-based editor),
+install the vscode-scheme
+extension so that parentheses are highlighted.
+
+
Before:
+
+
+
+
After:
+
+
+
+
In addition, the 61a-bot (installation instructions) VS Code extension is available for Scheme homeworks. The bot is also integrated into ok.
+
+
+
Required Questions
+
+
+
+
+
+
+
+
+
Getting Started Videos
+
+
+
These videos may provide some helpful direction for tackling the coding
+problems on this assignment.
+
+
To see these videos, you should be logged into your berkeley.edu email.
Recall that currying transforms a multiple argument function into a series of higher-order, one argument functions. In the next set of questions, you will be creating functions that can automatically curry a function of any length using the notion that programs are data!
+
+
+
Q1: Cooking Curry
+
+
+
Implement the function curry-cook, which takes in a Scheme list formals and a quoted expression body. curry-cook should generate a program as a list which is a curried version of a lambda function. The outputted program should be a curried version of a lambda function with formal arguments equal to formals, and a function body equal to body. You may assume that all functions passed in will have more than 0 formals; otherwise, it would not be curry-able!
+
+
For example, if you wanted to curry the function (lambda (x y) (+ x y)), you would set formals equal to '(x y), the body equal to '(+ x y), and make a call to curry-cook: (curry-cook '(x y) '(+ x y)).
+
+
scm> (curry-cook '(a) 'a)
+(lambda (a) a)
+scm> (curry-cook '(x y) '(+ x y))
+(lambda (x) (lambda (y) (+ x y)))
Implement the function curry-consume, which takes in a curried lambda function curry and applies the function to a list of arguments args. You may make the following assumptions:
+
+
+
If curry is an n-curried function, then there will be at most n arguments in args.
+
If there are 0 arguments (args is an empty list), then you may assume that curry has been fully applied with relevant arguments; in this case, curry now contains a value representing the output of the lambda function. Return it.
+
+
+
Note that there can be fewer args than formals for the corresponding lambda function curry! In the case that there are fewer arguments, curry-consume should return a curried lambda function, which is the result of partially applying curry up to the number of args provdied. See the doctests below for a few examples.
+
+
scm> (define three-curry (lambda (x) (lambda (y) (lambda (z) (+ x (* y z)))) ))
+three-curry
+scm> (define eat-two (curry-consume three-curry '(1 2))) ; pass in only two arguments, return should be a one-arg lambda function!
+eat-two
+scm> eat-two
+(lambda (z) (+ x (* y z)))
+scm> (eat-two 3) ; pass in the last argument; 1 + (2 * 3)
+7
+scm> (curry-consume three-curry '(1 2 3)) ; all three arguments at once
+7
switch is a macro that takes in an expression expr and a list of pairs, options, where the first element of each pair is some value and the second element is a single expression. switch evaluates the expression contained in the list of options that corresponds to the value that expr evaluates to.
+
+
scm> (switch (+ 1 1) ((1 (print 'a))
+ (2 (print 'b)) ; (print 'b) is evaluated because (+ 1 1) evaluates to 2
+ (3 (print 'c))))
+b
+
+
+
+
switch uses another procedure called switch-to-cond in its implementation:
Your task is to define switch-to-cond, which is a procedure (not a macro) that takes a quoted switch expression and converts it into a cond expression with the same behavior. An example is shown below.
You can locally check your score on each question of this assignment by running
+
+
python3 ok --score
+
+
This does NOT submit the assignment! When you are satisfied with your score, submit the assignment to Gradescope to receive credit for it.
+
+
Submit Assignment
+
+
+
Submit this assignment by uploading any files you've edited to the appropriate Gradescope assignment.Lab 00 has detailed instructions.
+
+
+
Exam Practice
+
+
+
Homework assignments will also contain prior exam questions for you to try.
+These questions have no submission component; feel free to attempt them if you'd like some practice!
Submission: When you are done, submit the assignment by uploading all code files you've edited to Gradescope. You may submit more than once before the deadline; only the
+final submission will be scored. Check that you have successfully submitted
+your code on Gradescope. See Lab 0 for more instructions on
+submitting assignments.
+
+
Using Ok: If you have any questions about using Ok, please
+refer to this guide.
+
+
+
+
Grading: Homework is graded based on
+correctness. Each incorrect problem will decrease the total score by one point.
+This homework is out of 2 points.
+
+
+
+
+
+
+
+
To check your progress, you can run sqlite3 directly by running:
+
+
python3 sqlite_shell.py --init hw10.sql
+
+
You should also check your work using ok:
+
+
python3 ok
+
+
+
Required Questions
+
+
+
+
+
+
+
+
+
+
+
Getting Started Videos
+
+
+
These videos may provide some helpful direction for tackling the coding
+problems on this assignment.
+
+
To see these videos, you should be logged into your berkeley.edu email.
In each question below, you will define a new table based on the following
+tables.
+
+
CREATE TABLE parents AS
+ SELECT "abraham" AS parent, "barack" AS child UNION
+ SELECT "abraham" , "clinton" UNION
+ SELECT "delano" , "herbert" UNION
+ SELECT "fillmore" , "abraham" UNION
+ SELECT "fillmore" , "delano" UNION
+ SELECT "fillmore" , "grover" UNION
+ SELECT "eisenhower" , "fillmore";
+
+CREATE TABLE dogs AS
+ SELECT "abraham" AS name, "long" AS fur, 26 AS height UNION
+ SELECT "barack" , "short" , 52 UNION
+ SELECT "clinton" , "long" , 47 UNION
+ SELECT "delano" , "long" , 46 UNION
+ SELECT "eisenhower" , "short" , 35 UNION
+ SELECT "fillmore" , "curly" , 32 UNION
+ SELECT "grover" , "short" , 28 UNION
+ SELECT "herbert" , "curly" , 31;
+
+CREATE TABLE sizes AS
+ SELECT "toy" AS size, 24 AS min, 28 AS max UNION
+ SELECT "mini" , 28 , 35 UNION
+ SELECT "medium" , 35 , 45 UNION
+ SELECT "standard" , 45 , 60;
+
+
Your tables should still perform correctly even if the values in these tables
+change. For example, if you are asked to list all dogs with a name that starts
+with h, you should write:
+
+
SELECT name FROM dogs WHERE "h" <= name AND name < "i";
+
+
Instead of assuming that the dogs table has only the data above and writing
+
+
SELECT "herbert";
+
+
The former query would still be correct if the name grover were changed to
+hoover or a row was added with the name harry.
+
+
+
Q1: By Parent Height
+
+
Create a table by_parent_height that has a column of the names of all dogs that have
+a parent, ordered by the height of the parent dog from tallest parent to shortest
+parent.
+
+
-- All dogs with parents ordered by decreasing height of their parent
+CREATE TABLE by_parent_height AS
+ SELECT "REPLACE THIS LINE WITH YOUR SOLUTION";
+
+
+
For example, fillmore has a parent eisenhower with height 35, and so
+should appear before grover who has a parent fillmore with height 32.
+The names of dogs with parents of the same height should appear together in any
+order. For example, barack and clinton should both appear at the end, but
+either one can come before the other.
The Fédération Cynologique Internationale classifies a standard poodle as over
+45 cm and up to 60 cm. The sizes table describes this and other such
+classifications, where a dog must be over the min and less than or equal to
+the max in height to qualify as size.
+
+
Create a size_of_dogs table with two columns, one for each dog's name and
+another for its size.
+
+
-- The size of each dog
+CREATE TABLE size_of_dogs AS
+ SELECT "REPLACE THIS LINE WITH YOUR SOLUTION";
+
There are two pairs of siblings that have the same size. Create a table that
+contains a row with a string for each of these pairs. Each string should be a
+sentence describing the siblings by their size.
+
+
-- Filling out this helper table is optional
+CREATE TABLE siblings AS
+ SELECT "REPLACE THIS LINE WITH YOUR SOLUTION";
+
+-- Sentences about siblings that are the same size
+CREATE TABLE sentences AS
+ SELECT "REPLACE THIS LINE WITH YOUR SOLUTION";
+
+
+
Each sibling pair should appear only once in the output, and siblings should be
+listed in alphabetical order (e.g. "barack and clinton..." instead of
+"clinton and barack..."), as follows:
+
+
sqlite> SELECT * FROM sentences;
+The two siblings, barack and clinton, have the same size: standard
+The two siblings, abraham and grover, have the same size: toy
+
+
Hint: First, create a helper table containing each pair of siblings. This
+will make comparing the sizes of siblings when constructing the main table
+easier.
+
+
Hint: If you join a table with itself, use AS within the FROM clause to
+give each table an alias.
+
+
Hint: In order to concatenate two strings into one, use the || operator.
+
+
+
+
Use Ok to test your code:
python3 ok -q sentences
+
+
+
+
+
+
+
Q4: Low Variance
+
+
+
We want to create a table that contains the height range (difference between maximum and minimum height) of all dogs that share a fur type. However, we'll only
+consider fur types where each dog with that fur type is within 30% of the average height of all dogs with that fur type.
+
+
For example, if the average height for short-haired dogs is 10, then in order to be included in our
+output, all dogs with short hair must have a height of at most 13 and at least 7.
+
+
To achieve this, we can use MIN, MAX, and AVG.
+For this problem, we'll want to find the average height and make sure that:
+
+
+
There are no heights smaller than 0.7 of the average.
+
There are no heights greater than 1.3 of the average.
+
+
+
Your output should first include the fur type and then the height range for the fur types that meet
+this criteria.
+
+
-- Height range for each fur type where all of the heights differ by no more than 30% from the average height
+CREATE TABLE low_variance AS
+ SELECT "REPLACE THIS LINE WITH YOUR SOLUTION";
+
+-- Example:
+SELECT * FROM low_variance;
+-- Expected output:
+-- curly|1
+
+
Explanation: The average height of long-haired dogs is 39.7, so the low variance criterion requires the height of each long-haired dog to be between 27.8 and 51.6. However, abraham is a long-haired dog with height 26, which is outside this range. For short-haired dogs, barack falls outside the valid range (check!). Thus, neither short nor long haired dogs are included in the output. There are two curly haired dogs: fillmore with height 32 and herbert with height 31. This gives a height range of 1.
+
+
Use Ok to test your code:
python3 ok -q low_variance
+
+
+
+
+
Submit Assignment
+
+
+
Submit this assignment by uploading any files you've edited to the appropriate Gradescope assignment.Lab 00 has detailed instructions.
+
+
Make sure to submit hw10.sql to the autograder!
+
+
+
Exam Practice
+
+
+
The following are some SQL exam problems from previous semesters that you may find useful as additional exam practice.
If 80% or more students (computed as the fraction of the number of students who took Midterm 2)
+complete all three, then all students who complete this homework get an extra credit point!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hw/hw11/ok b/hw/hw11/ok
new file mode 100644
index 0000000000..593354ea21
Binary files /dev/null and b/hw/hw11/ok differ
diff --git a/hw/sol-hw01/hw01.zip b/hw/sol-hw01/hw01.zip
index d7d92aa717..06b47c4482 100644
Binary files a/hw/sol-hw01/hw01.zip and b/hw/sol-hw01/hw01.zip differ
diff --git a/hw/sol-hw01/ok b/hw/sol-hw01/ok
index b84a800fe7..593354ea21 100644
Binary files a/hw/sol-hw01/ok and b/hw/sol-hw01/ok differ
diff --git a/hw/sol-hw02/hw02.zip b/hw/sol-hw02/hw02.zip
index 5a211d28cc..682588c8ed 100644
Binary files a/hw/sol-hw02/hw02.zip and b/hw/sol-hw02/hw02.zip differ
diff --git a/hw/sol-hw02/ok b/hw/sol-hw02/ok
index b84a800fe7..593354ea21 100644
Binary files a/hw/sol-hw02/ok and b/hw/sol-hw02/ok differ
diff --git a/hw/sol-hw03/hw03.zip b/hw/sol-hw03/hw03.zip
index e6724bb376..fc113712e1 100644
Binary files a/hw/sol-hw03/hw03.zip and b/hw/sol-hw03/hw03.zip differ
diff --git a/hw/sol-hw03/ok b/hw/sol-hw03/ok
index b84a800fe7..593354ea21 100644
Binary files a/hw/sol-hw03/ok and b/hw/sol-hw03/ok differ
diff --git a/hw/sol-hw04/assets/mobile-planet-labeled.png b/hw/sol-hw04/assets/mobile-planet-labeled.png
new file mode 100644
index 0000000000..0720a59378
Binary files /dev/null and b/hw/sol-hw04/assets/mobile-planet-labeled.png differ
diff --git a/hw/sol-hw04/assets/mobile-planet.png b/hw/sol-hw04/assets/mobile-planet.png
new file mode 100644
index 0000000000..e49f4e6364
Binary files /dev/null and b/hw/sol-hw04/assets/mobile-planet.png differ
diff --git a/hw/sol-hw04/construct_check.py b/hw/sol-hw04/construct_check.py
new file mode 100644
index 0000000000..bee096e7e4
--- /dev/null
+++ b/hw/sol-hw04/construct_check.py
@@ -0,0 +1,180 @@
+from ast import parse, NodeVisitor, Name
+
+# For error messages (student-facing) only
+_NAMES = {
+ 'Add': '+',
+ 'And': 'and',
+ 'Assert': 'assert',
+ 'Assign': '=',
+ 'AnnAssign': '=',
+ 'AugAssign': 'op=',
+ 'BitAnd': '&',
+ 'BitOr': '|',
+ 'BitXor': '^',
+ 'Break': 'break',
+ 'Recursion': 'recursive call',
+ 'ClassDef': 'class',
+ 'Continue': 'continue',
+ 'Del': 'del',
+ 'Delete': 'delete',
+ 'Dict': '{...}',
+ 'DictComp': '{...}',
+ 'Div': '/',
+ 'Ellipsis': '...',
+ 'Eq': '==',
+ 'ExceptHandler': 'except',
+ 'ExtSlice': '[::]',
+ 'FloorDiv': '//',
+ 'For': 'for',
+ 'FunctionDef': 'def',
+ 'Filter': 'filter',
+ 'GeneratorExp': '(... for ...)',
+ 'Global': 'global',
+ 'Gt': '>',
+ 'GtE': '>=',
+ 'If': 'if',
+ 'IfExp': '...if...else...',
+ 'Import': 'import',
+ 'ImportFrom': 'from ... import ...',
+ 'In': 'in',
+ 'Index': '...[...]',
+ 'Invert': '~',
+ 'Is': 'is',
+ 'IsNot': 'is not ',
+ 'LShift': '<<',
+ 'Lambda': 'lambda',
+ 'List': '[...]',
+ 'ListComp': '[...for...]',
+ 'Lt': '<',
+ 'LtE': '<=',
+ 'Mod': '%',
+ 'Mult': '*',
+ 'NamedExpr': ':=',
+ 'Nonlocal': 'nonlocal',
+ 'Not': 'not',
+ 'NotEq': '!=',
+ 'NotIn': 'not in',
+ 'Or': 'or',
+ 'Pass': 'pass',
+ 'Pow': '**',
+ 'RShift': '>>',
+ 'Raise': 'raise',
+ 'Return': 'return',
+ 'Set': '{ ... } (set)',
+ 'SetComp': '{ ... for ... } (set)',
+ 'Slice': '[ : ]',
+ 'Starred': '',
+ 'Str': 'str',
+ 'Sub': '-',
+ 'Subscript': '[]',
+ 'Try': 'try',
+ 'Tuple': '(... , ... )',
+ 'UAdd': '+',
+ 'USub': '-',
+ 'While': 'while',
+ 'With': 'with',
+ 'Yield': 'yield',
+ 'YieldFrom': 'yield from',
+}
+
+def check(source_file, checked_funcs, disallow, source=None):
+ """Checks that AST nodes whose type names are present in DISALLOW
+ (an object supporting 'in') are not present in the function(s) named
+ CHECKED_FUNCS in SOURCE. By default, SOURCE is the contents of the
+ file SOURCE_FILE. CHECKED_FUNCS is either a string (indicating a single
+ name) or an object of some other type that supports 'in'. CHECKED_FUNCS
+ may contain __main__ to indicate an entire module. Prints reports of
+ each prohibited node and returns True iff none are found.
+ See ast.__dir__() for AST type names. The special node name 'Recursion'
+ checks for overtly recursive calls (i.e., calls of the form NAME(...) where
+ NAME is an enclosing def."""
+ return ExclusionChecker(disallow).check(source_file, checked_funcs, source)
+
+class ExclusionChecker(NodeVisitor):
+ """An AST visitor that checks that certain constructs are excluded from
+ parts of a program. ExclusionChecker(EXC) checks that AST node types
+ whose names are in the sequence or set EXC are not present. Its check
+ method visits nodes in a given function of a source file checking that the
+ indicated node types are not used."""
+
+ def __init__(self, disallow=()):
+ """DISALLOW is the initial default list of disallowed
+ node-type names."""
+ self._disallow = set(disallow)
+ self._checking = False
+ self._errs = 0
+
+ def generic_visit(self, node):
+ if self._checking and type(node).__name__ in self._disallow:
+ self._report(node)
+ super().generic_visit(node)
+
+ def visit_Module(self, node):
+ if "__main__" in self._checked_funcs:
+ self._checking = True
+ self._checked_name = self._source_file
+ super().generic_visit(node)
+
+ def visit_Call(self, node):
+ if 'Recursion' in self._disallow and \
+ type(node.func) is Name and \
+ node.func.id in self._func_nest:
+ self._report(node, "should not be recursive")
+ self.generic_visit(node)
+
+ def visit_FunctionDef(self, node):
+ self._func_nest.append(node.name)
+ if self._checking:
+ self.generic_visit(node)
+ elif node.name in self._checked_funcs:
+ self._checked_name = "Function " + node.name
+ checking0 = self._checking
+ self._checking = True
+ super().generic_visit(node)
+ self._checking = checking0
+ self._func_nest.pop()
+
+ def _report(self, node, msg=None):
+ node_name = _NAMES.get(type(node).__name__, type(node).__name__)
+ if msg is None:
+ msg = "should not contain '{}'".format(node_name)
+ print("{} {}".format(self._checked_name, msg))
+ self._errs += 1
+
+ def errors(self):
+ """Returns the number of number of prohibited constructs found in
+ the last call to check."""
+ return self._errs
+
+ def check(self, source_file, checked_funcs, disallow=None, source=None):
+ """Checks that AST nodes whose type names are present in DISALLOW
+ (an object supporting the contains test) are not present in
+ the function(s) named CHECKED_FUNCS in SOURCE. By default, SOURCE
+ is the contents of the file SOURCE_FILE. DISALLOW defaults to the
+ argument given to the constructor (and resets that value if it is
+ present). CHECKED_FUNCS is either a string (indicating a single
+ name) or an object of some other type that supports 'in'.
+ CHECKED_FUNCS may contain __main__ to indicate an entire module.
+ Prints reports of each prohibited node and returns True iff none
+ are found.
+ See ast.__dir__() for AST type names. The special node name
+ 'Recursion' checks for overtly recursive calls (i.e., calls of the
+ form NAME(...) where NAME is an enclosing def."""
+
+ self._checking = False
+ self._source_file = source_file
+ self._func_nest = []
+ if type(checked_funcs) is str:
+ self._checked_funcs = { checked_funcs }
+ else:
+ self._checked_funcs = set(checked_funcs)
+ if disallow is not None:
+ self._disallow = set(disallow)
+ if source is None:
+ with open(source_file, 'r', errors='ignore') as inp:
+ source = inp.read()
+ p = parse(source, source_file)
+ self._errs = 0
+
+ self.visit(p)
+ return self._errs == 0
\ No newline at end of file
diff --git a/hw/sol-hw04/hw04.ok b/hw/sol-hw04/hw04.ok
new file mode 100644
index 0000000000..0c6a617d18
--- /dev/null
+++ b/hw/sol-hw04/hw04.ok
@@ -0,0 +1,26 @@
+{
+ "name": "Homework 4",
+ "endpoint": "cal/cs61a/fa24/hw04",
+ "src": [
+ "hw04.py"
+ ],
+ "tests": {
+ "hw*.py": "doctest",
+ "tests/*.py": "ok_test"
+ },
+ "default_tests": [
+ "deep_map",
+ "total_mass",
+ "balanced",
+ "max_path_sum"
+ ],
+ "protocols": [
+ "restore",
+ "file_contents",
+ "analytics",
+ "help",
+ "unlock",
+ "grading",
+ "backup"
+ ]
+}
\ No newline at end of file
diff --git a/hw/sol-hw04/hw04.py b/hw/sol-hw04/hw04.py
new file mode 100644
index 0000000000..9898785364
--- /dev/null
+++ b/hw/sol-hw04/hw04.py
@@ -0,0 +1,234 @@
+def deep_map(f, s):
+ """Replace all non-list elements x with f(x) in the nested list s.
+
+ >>> six = [1, 2, [3, [4], 5], 6]
+ >>> deep_map(lambda x: x * x, six)
+ >>> six
+ [1, 4, [9, [16], 25], 36]
+ >>> # Check that you're not making new lists
+ >>> s = [3, [1, [4, [1]]]]
+ >>> s1 = s[1]
+ >>> s2 = s1[1]
+ >>> s3 = s2[1]
+ >>> deep_map(lambda x: x + 1, s)
+ >>> s
+ [4, [2, [5, [2]]]]
+ >>> s1 is s[1]
+ True
+ >>> s2 is s1[1]
+ True
+ >>> s3 is s2[1]
+ True
+ """
+ for i in range(len(s)):
+ if type(s[i]) == list:
+ deep_map(f, s[i])
+ else:
+ s[i] = f(s[i])
+
+
+HW_SOURCE_FILE=__file__
+
+
+def planet(mass):
+ """Construct a planet of some mass."""
+ assert mass > 0
+ return ['planet', mass]
+
+def mass(p):
+ """Select the mass of a planet."""
+ assert is_planet(p), 'must call mass on a planet'
+ return p[1]
+
+def is_planet(p):
+ """Whether p is a planet."""
+ return type(p) == list and len(p) == 2 and p[0] == 'planet'
+
+def examples():
+ t = mobile(arm(1, planet(2)),
+ arm(2, planet(1)))
+ u = mobile(arm(5, planet(1)),
+ arm(1, mobile(arm(2, planet(3)),
+ arm(3, planet(2)))))
+ v = mobile(arm(4, t), arm(2, u))
+ return t, u, v
+
+def total_mass(m):
+ """Return the total mass of m, a planet or mobile.
+
+ >>> t, u, v = examples()
+ >>> total_mass(t)
+ 3
+ >>> total_mass(u)
+ 6
+ >>> total_mass(v)
+ 9
+ """
+ if is_planet(m):
+ return mass(m)
+ else:
+ assert is_mobile(m), "must get total mass of a mobile or a planet"
+ return total_mass(end(left(m))) + total_mass(end(right(m)))
+
+def balanced(m):
+ """Return whether m is balanced.
+
+ >>> t, u, v = examples()
+ >>> balanced(t)
+ True
+ >>> balanced(v)
+ True
+ >>> p = mobile(arm(3, t), arm(2, u))
+ >>> balanced(p)
+ False
+ >>> balanced(mobile(arm(1, v), arm(1, p)))
+ False
+ >>> balanced(mobile(arm(1, p), arm(1, v)))
+ False
+ >>> from construct_check import check
+ >>> # checking for abstraction barrier violations by banning indexing
+ >>> check(HW_SOURCE_FILE, 'balanced', ['Index'])
+ True
+ """
+ if is_planet(m):
+ return True
+ else:
+ left_end, right_end = end(left(m)), end(right(m))
+ torque_left = length(left(m)) * total_mass(left_end)
+ torque_right = length(right(m)) * total_mass(right_end)
+ return torque_left == torque_right and balanced(left_end) and balanced(right_end)
+
+
+HW_SOURCE_FILE=__file__
+
+
+def max_path_sum(t):
+ """Return the maximum root-to-leaf path sum of a tree.
+ >>> t = tree(1, [tree(5, [tree(1), tree(3)]), tree(10)])
+ >>> max_path_sum(t) # 1, 10
+ 11
+ >>> t2 = tree(5, [tree(4, [tree(1), tree(3)]), tree(2, [tree(10), tree(3)])])
+ >>> max_path_sum(t2) # 5, 2, 10
+ 17
+ """
+ # Non-list comprehension solution
+ if is_leaf(t):
+ return label(t)
+ highest_sum = 0
+ for b in branches(t):
+ highest_sum = max(max_path_sum(b), highest_sum)
+ return label(t) + highest_sum
+
+ # List comprehension solution
+ if is_leaf(t):
+ return label(t)
+ else:
+ return label(t) + max([max_path_sum(b) for b in branches(t)])
+
+
+def mobile(left, right):
+ """Construct a mobile from a left arm and a right arm."""
+ assert is_arm(left), "left must be an arm"
+ assert is_arm(right), "right must be an arm"
+ return ['mobile', left, right]
+
+def is_mobile(m):
+ """Return whether m is a mobile."""
+ return type(m) == list and len(m) == 3 and m[0] == 'mobile'
+
+def left(m):
+ """Select the left arm of a mobile."""
+ assert is_mobile(m), "must call left on a mobile"
+ return m[1]
+
+def right(m):
+ """Select the right arm of a mobile."""
+ assert is_mobile(m), "must call right on a mobile"
+ return m[2]
+
+def arm(length, mobile_or_planet):
+ """Construct an arm: a length of rod with a mobile or planet at the end."""
+ assert is_mobile(mobile_or_planet) or is_planet(mobile_or_planet)
+ return ['arm', length, mobile_or_planet]
+
+def is_arm(s):
+ """Return whether s is an arm."""
+ return type(s) == list and len(s) == 3 and s[0] == 'arm'
+
+def length(s):
+ """Select the length of an arm."""
+ assert is_arm(s), "must call length on an arm"
+ return s[1]
+
+def end(s):
+ """Select the mobile or planet hanging at the end of an arm."""
+ assert is_arm(s), "must call end on an arm"
+ return s[2]
+
+
+
+# Tree Data Abstraction
+
+def tree(label, branches=[]):
+ """Construct a tree with the given label value and a list of branches."""
+ for branch in branches:
+ assert is_tree(branch), 'branches must be trees'
+ return [label] + list(branches)
+
+def label(tree):
+ """Return the label value of a tree."""
+ return tree[0]
+
+def branches(tree):
+ """Return the list of branches of the given tree."""
+ return tree[1:]
+
+def is_tree(tree):
+ """Returns True if the given tree is a tree, and False otherwise."""
+ if type(tree) != list or len(tree) < 1:
+ return False
+ for branch in branches(tree):
+ if not is_tree(branch):
+ return False
+ return True
+
+def is_leaf(tree):
+ """Returns True if the given tree's list of branches is empty, and False
+ otherwise.
+ """
+ return not branches(tree)
+
+def print_tree(t, indent=0):
+ """Print a representation of this tree in which each node is
+ indented by two spaces times its depth from the root.
+
+ >>> print_tree(tree(1))
+ 1
+ >>> print_tree(tree(1, [tree(2)]))
+ 1
+ 2
+ >>> numbers = tree(1, [tree(2), tree(3, [tree(4), tree(5)]), tree(6, [tree(7)])])
+ >>> print_tree(numbers)
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ """
+ print(' ' * indent + str(label(t)))
+ for b in branches(t):
+ print_tree(b, indent + 1)
+
+def copy_tree(t):
+ """Returns a copy of t. Only for testing purposes.
+
+ >>> t = tree(5)
+ >>> copy = copy_tree(t)
+ >>> t = tree(6)
+ >>> print_tree(copy)
+ 5
+ """
+ return tree(label(t), [copy_tree(b) for b in branches(t)])
+
diff --git a/hw/sol-hw04/hw04.zip b/hw/sol-hw04/hw04.zip
new file mode 100644
index 0000000000..1beda826e7
Binary files /dev/null and b/hw/sol-hw04/hw04.zip differ
diff --git a/hw/sol-hw04/index.html b/hw/sol-hw04/index.html
new file mode 100644
index 0000000000..ce3230f0a6
--- /dev/null
+++ b/hw/sol-hw04/index.html
@@ -0,0 +1,730 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Homework 4 Solutions | CS 61A Fall 2024
+
+
+
+
+
+
+
+
+
Write a function deep_map that takes a list s and a one-argument function
+f. s may be a nested list, one that contain other lists. deep_map modifies
+s by replacing each element within s or any of the lists it contains with
+the result of calling f on that element.
+
+
deep_map returns None and should not create any new lists.
+
+
Hint:type(a) == list will evaluate to True if a is a list.
+
+
+
+
def deep_map(f, s):
+ """Replace all non-list elements x with f(x) in the nested list s.
+
+ >>> six = [1, 2, [3, [4], 5], 6]
+ >>> deep_map(lambda x: x * x, six)
+ >>> six
+ [1, 4, [9, [16], 25], 36]
+ >>> # Check that you're not making new lists
+ >>> s = [3, [1, [4, [1]]]]
+ >>> s1 = s[1]
+ >>> s2 = s1[1]
+ >>> s3 = s2[1]
+ >>> deep_map(lambda x: x + 1, s)
+ >>> s
+ [4, [2, [5, [2]]]]
+ >>> s1 is s[1]
+ True
+ >>> s2 is s1[1]
+ True
+ >>> s3 is s2[1]
+ True
+ """
+
for i in range(len(s)):
+ if type(s[i]) == list:
+ deep_map(f, s[i])
+ else:
+ s[i] = f(s[i])
+
+
+
+
Use Ok to test your code:
python3 ok -q deep_map
+
+
+
+
+
Data Abstraction
+
+
+
+
Mobiles
+
+
+
Acknowledgements
+
+
This problem is based on one from Structure and Interpretation of Computer Programs
+Section 2.2.2.
+
+
+
+
We are making a planetarium mobile. A mobile is a type of hanging sculpture. A binary mobile consists of two
+arms. Each arm is a rod of a certain length, from which hangs either a planet
+or another mobile. For example, the below diagram shows the left and right arms of Mobile A,
+and what hangs at the ends of each of those arms.
+
+
+
+
We will represent a binary mobile using the data abstractions below.
+
+
+
A mobile must have both a left arm and a right arm.
+
An arm has a positive length and must have something hanging at the end, either a mobile or planet.
+
A planet has a positive mass, and nothing hanging from it.
+
+
+
Below are the various constructors and selectors for the mobile and arm data abstraction.
+They have already been implemented for you, though the code is not shown here.
+As with any data abstraction, you should focus on what the function does rather than its specific implementation.
+You are free to use any of their constructor and selector functions in the Mobiles coding exercises.
+
+
Mobile Data Abstraction (for your reference, no need to do anything here):
+
+
def mobile(left, right):
+ """
+ Construct a mobile from a left arm and a right arm.
+
+ Arguments:
+ left: An arm representing the left arm of the mobile.
+ right: An arm representing the right arm of the mobile.
+
+ Returns:
+ A mobile constructed from the left and right arms.
+ """
+ pass
+
+def is_mobile(m):
+ """
+ Return whether m is a mobile.
+
+ Arguments:
+ m: An object to be checked.
+
+ Returns:
+ True if m is a mobile, False otherwise.
+ """
+ pass
+
+def left(m):
+ """
+ Select the left arm of a mobile.
+
+ Arguments:
+ m: A mobile.
+
+ Returns:
+ The left arm of the mobile.
+ """
+ pass
+
+def right(m):
+ """
+ Select the right arm of a mobile.
+
+ Arguments:
+ m: A mobile.
+
+ Returns:
+ The right arm of the mobile.
+ """
+ pass
+
+
+Arm Data Abstraction (for your reference, no need to do anything here):
+
+
def arm(length, mobile_or_planet):
+ """
+ Construct an arm: a length of rod with a mobile or planet at the end.
+
+ Arguments:
+ length: The length of the rod.
+ mobile_or_planet: A mobile or a planet at the end of the arm.
+
+ Returns:
+ An arm constructed from the given length and mobile or planet.
+ """
+ pass
+
+def is_arm(s):
+ """
+ Return whether s is an arm.
+
+ Arguments:
+ s: An object to be checked.
+
+ Returns:
+ True if s is an arm, False otherwise.
+ """
+ pass
+
+def length(s):
+ """
+ Select the length of an arm.
+
+ Arguments:
+ s: An arm.
+
+ Returns:
+ The length of the arm.
+ """
+ pass
+
+def end(s):
+ """
+ Select the mobile or planet hanging at the end of an arm.
+
+ Arguments:
+ s: An arm.
+
+ Returns:
+ The mobile or planet at the end of the arm.
+ """
+ pass
+
+
+
Q2: Mass
+
+
+
Implement the planet data abstraction by completing the planet constructor
+and the mass selector. A planet should be represented using a two-element list
+where the first element is the string 'planet' and the second element is the
+planet's mass. The mass function should return the mass of the planet object
+that is passed as a parameter.
+
+
+
def planet(mass):
+ """Construct a planet of some mass."""
+ assert mass > 0
+
return ['planet', mass]
+def mass(p):
+ """Select the mass of a planet."""
+ assert is_planet(p), 'must call mass on a planet'
+
return p[1]
+def is_planet(p):
+ """Whether p is a planet."""
+ return type(p) == list and len(p) == 2 and p[0] == 'planet'
+
+
The total_mass function demonstrates the use of the mobile, arm,
+and planet abstractions. It has been implemented for you. You may use
+the total_mass function in the following questions.
+
+
+
def examples():
+ t = mobile(arm(1, planet(2)),
+ arm(2, planet(1)))
+ u = mobile(arm(5, planet(1)),
+ arm(1, mobile(arm(2, planet(3)),
+ arm(3, planet(2)))))
+ v = mobile(arm(4, t), arm(2, u))
+ return t, u, v
+
+def total_mass(m):
+ """Return the total mass of m, a planet or mobile.
+
+ >>> t, u, v = examples()
+ >>> total_mass(t)
+ 3
+ >>> total_mass(u)
+ 6
+ >>> total_mass(v)
+ 9
+ """
+ if is_planet(m):
+ return mass(m)
+ else:
+ assert is_mobile(m), "must get total mass of a mobile or a planet"
+ return total_mass(end(left(m))) + total_mass(end(right(m)))
+
+
Run the ok tests for total_mass to make sure that your planet and mass
+functions are implemented correctly.
+
+
Use Ok to test your code:
python3 ok -q total_mass
+
+
+
+
+
Q3: Balanced
+
+
+
Implement the balanced function, which returns whether m is a balanced
+mobile. A mobile is balanced if both of the following conditions are met:
+
+
+
The torque applied by its left arm is equal to the torque applied by its right
+ arm. The torque of the left arm is the length of the left rod multiplied by the
+ total mass hanging from that rod. Likewise for the right. For example,
+ if the left arm has a length of 5, and there is a mobile hanging at the end
+ of the left arm of total mass 10, the torque on the left side of our mobile is 50.
+
Each of the mobiles hanging at the end of its arms is itself balanced.
+
+
+
Planets themselves are balanced, as there is nothing hanging off of them.
+
+
Reminder: You may use the total_mass function above. Don't violate
+abstraction barriers. Instead, use the selector functions that have been
+defined.
The fact that planets are balanced is important, since we will be solving
+this recursively like many other tree problems (even though this is not
+explicitly a tree).
+
+
+
Base case: if we are checking a planet, then we know that this is
+ balanced. Why is this an appropriate base case? There are two possible
+ approaches to this:
+
+
+
Because we know that our data structures so far are trees, planets
+ are the simplest possible tree since we have chosen to implement
+ them as leaves.
+
We also know that from a data abstraction standpoint, planets are the terminal
+ item in a mobile. There can be no further mobile structures under
+ this planet, so it makes sense to stop check here.
+
+
Otherwise: note that it is important to do a recursive call to check
+ if both arms are balanced. However, we also need to do the basic
+ comparison of looking at the total mass of both arms as well as their
+ length. For example if both arms are a planet, trivially, they will
+ both be balanced. However, the torque must be equal in order for the
+ entire mobile to balanced (i.e. it's insufficient to just check if the
+ arms are balanced).
+
+
+
+
+
+
Trees
+
+
+
+
Q4: Maximum Path Sum
+
+
+
Write a function that takes in a tree and returns the maximum sum of the
+values along any root-to-leaf path in the tree.
+A root-to-leaf path is a sequence of nodes starting at the root and proceeding
+to some leaf of the tree.
+You can assume the tree will have positive numbers for its labels.
+
+
+
+
def max_path_sum(t):
+ """Return the maximum root-to-leaf path sum of a tree.
+ >>> t = tree(1, [tree(5, [tree(1), tree(3)]), tree(10)])
+ >>> max_path_sum(t) # 1, 10
+ 11
+ >>> t2 = tree(5, [tree(4, [tree(1), tree(3)]), tree(2, [tree(10), tree(3)])])
+ >>> max_path_sum(t2) # 5, 2, 10
+ 17
+ """
+
# Non-list comprehension solution
+ if is_leaf(t):
+ return label(t)
+ highest_sum = 0
+ for b in branches(t):
+ highest_sum = max(max_path_sum(b), highest_sum)
+ return label(t) + highest_sum
+
+ # List comprehension solution
+ if is_leaf(t):
+ return label(t)
+ else:
+ return label(t) + max([max_path_sum(b) for b in branches(t)])
+
+
+
+
Use Ok to test your code:
python3 ok -q max_path_sum
+
+
+
+
+
Check Your Score Locally
+
+
You can locally check your score on each question of this assignment by running
+
+
python3 ok --score
+
+
This does NOT submit the assignment! When you are satisfied with your score, submit the assignment to Gradescope to receive credit for it.
+
+
+
Submit Assignment
+
+
+
Submit this assignment by uploading any files you've edited to the appropriate Gradescope assignment.Lab 00 has detailed instructions.
+
+
+
Exam Practice
+
+
Homework assignments will also contain prior exam-level questions for you to take a look at.
+ These questions have no submission component; feel free to attempt them if you'd like a challenge!
Write a generator function that yields the elements of the hailstone sequence starting at number n.
+After reaching the end of the hailstone sequence, the generator should yield the value 1 indefinitely.
+
+
Here's a quick reminder of how the hailstone sequence is defined:
+
+
+
Pick a positive integer n as the start.
+
If n is even, divide it by 2.
+
If n is odd, multiply it by 3 and add 1.
+
Continue this process until n is 1.
+
+
+
Try to write this generator function recursively. If you're stuck, you can first try writing it iteratively
+and then seeing how you can turn that implementation into a recursive one.
+
+
Hint: Since hailstone returns a generator, you can yield from a call to hailstone!
+
+
+
+
def hailstone(n):
+ """Q1: Yields the elements of the hailstone sequence starting at n.
+ At the end of the sequence, yield 1 infinitely.
+
+ >>> hail_gen = hailstone(10)
+ >>> [next(hail_gen) for _ in range(10)]
+ [10, 5, 16, 8, 4, 2, 1, 1, 1, 1]
+ >>> next(hail_gen)
+ 1
+ """
+
yield n
+ if n == 1:
+ yield from hailstone(n)
+ elif n % 2 == 0:
+ yield from hailstone(n // 2)
+ else:
+ yield from hailstone(n * 3 + 1)
+
+
+
+
Use Ok to test your code:
python3 ok -q hailstone
+
+
+
+
+
Q2: Merge
+
+
Write a generator function merge that takes in two infinite generators a and b that are in increasing order without duplicates and returns a generator that has all the elements of both generators, in increasing order, without duplicates.
Define a generator function yield_paths which takes in a tree t, a value
+value, and returns a generator object which yields each path from the root
+of t to a node that has label value.
+
+
Each path should be represented as a list of the labels along that path in the
+tree. You may yield the paths in any order.
+
+
def yield_paths(t, value):
+ """Q4: Yields all possible paths from the root of t to a node with the label
+ value as a list.
+
+ >>> t1 = tree(1, [tree(2, [tree(3), tree(4, [tree(6)]), tree(5)]), tree(5)])
+ >>> print_tree(t1)
+ 1
+ 2
+ 3
+ 4
+ 6
+ 5
+ 5
+ >>> next(yield_paths(t1, 6))
+ [1, 2, 4, 6]
+ >>> path_to_5 = yield_paths(t1, 5)
+ >>> sorted(list(path_to_5))
+ [[1, 2, 5], [1, 5]]
+
+ >>> t2 = tree(0, [tree(2, [t1])])
+ >>> print_tree(t2)
+ 0
+ 2
+ 1
+ 2
+ 3
+ 4
+ 6
+ 5
+ 5
+ >>> path_to_2 = yield_paths(t2, 2)
+ >>> sorted(list(path_to_2))
+ [[0, 2], [0, 2, 1, 2]]
+ """
+ if label(t) == value:
+
yield [value]
for b in branches(t):
+
for path in yield_paths(b, value):
yield [label(t)] + path
+
+
Hint: If you're having trouble getting started, think about how you'd approach this
+problem if it wasn't a generator function. What would your recursive calls be?
+With a generator function, what happens if you make a "recursive call" within its body?
+
+
+
+
Hint: Try coming up with a few simple cases of your own! How should this function work when t is a leaf node?
+
+
+
+
Hint: Remember, it's possible to loop over generator objects because generators are iterators!
+
+
+
+
Note: Remember that this problem should yield paths -- do not return a list of paths!
+
+
+
+
Use Ok to test your code:
python3 ok -q yield_paths
+
+
+
+
+
+
If our current label is equal to value, we've found a path from the root to a node
+containing value containing only our current label, so we should yield that. From there,
+we'll see if there are any paths starting from one of our branches that ends at a
+node containing value. If we find these "partial paths" we can simply add our current
+label to the beinning of a path to obtain a path starting from the root.
+
+
In order to do this, we'll create a generator for each of the branches which yields
+these "partial paths". By calling yield_paths on each of the branches, we'll create
+exactly this generator! Then, since a generator is also an iterable, we can iterate over
+the paths in this generator and yield the result of concatenating it with our current label.
+
+
+
+
+
Check Your Score Locally
+
+
You can locally check your score on each question of this assignment by running
+
+
python3 ok --score
+
+
This does NOT submit the assignment! When you are satisfied with your score, submit the assignment to Gradescope to receive credit for it.
+
+
+
Submit Assignment
+
+
+
Submit this assignment by uploading any files you've edited to the appropriate Gradescope assignment.Lab 00 has detailed instructions.
+
+
+
Exam Practice
+
+
+
Homework assignments will also contain prior exam questions for you to try.
+These questions have no submission component; feel free to attempt them if you'd like some practice!
Confidentiality:
+Your responses to the survey are confidential, and only the instructors
+will be able to see this data unanonymized.
+More specifics on confidentiality can be found on the survey itself.
+
+
Once you finish the survey, you will be presented with a passphrase (if you miss
+it, it should also be at the bottom of the confirmation email you receive). Put
+this passphrase, as a string, on the line that says
+passphrase = 'REPLACE_THIS_WITH_PASSPHRASE' in the Python file for this assignment.
+E.g. if the passphrase is abc, then the line should be passphrase = 'abc'.
+
+
Use Ok to test your code:
python3 ok -q midsem_survey
+
+
+
+
+
OOP
+
+
+
Q2: Vending Machine
+
+
+
In this question you'll create a vending machine that sells a single product and provides change when needed.
+
+
Create a class called VendingMachine that represents a vending
+machine for some product. A VendingMachine object returns strings
+describing its interactions. Make sure your output exactly matches the strings in the doctests including punctuation and spacing!
+
+
You may find Python's formatted string literals, or f-strings useful.
+A quick example:
Fill in the VendingMachine class, adding attributes and methods as appropriate, such that its behavior matches the following doctests:
+
+
+
+
class VendingMachine:
+ """A vending machine that vends some product for some price.
+
+ >>> v = VendingMachine('candy', 10)
+ >>> v.vend()
+ 'Nothing left to vend. Please restock.'
+ >>> v.add_funds(15)
+ 'Nothing left to vend. Please restock. Here is your $15.'
+ >>> v.restock(2)
+ 'Current candy stock: 2'
+ >>> v.vend()
+ 'Please add $10 more funds.'
+ >>> v.add_funds(7)
+ 'Current balance: $7'
+ >>> v.vend()
+ 'Please add $3 more funds.'
+ >>> v.add_funds(5)
+ 'Current balance: $12'
+ >>> v.vend()
+ 'Here is your candy and $2 change.'
+ >>> v.add_funds(10)
+ 'Current balance: $10'
+ >>> v.vend()
+ 'Here is your candy.'
+ >>> v.add_funds(15)
+ 'Nothing left to vend. Please restock. Here is your $15.'
+
+ >>> w = VendingMachine('soda', 2)
+ >>> w.restock(3)
+ 'Current soda stock: 3'
+ >>> w.restock(3)
+ 'Current soda stock: 6'
+ >>> w.add_funds(2)
+ 'Current balance: $2'
+ >>> w.vend()
+ 'Here is your soda.'
+ """
+
def __init__(self, product, price):
+ self.product = product
+ self.price = price
+ self.stock = 0
+ self.balance = 0
+
+ def restock(self, n):
+ self.stock += n
+ return f'Current {self.product} stock: {self.stock}'
+
+ def add_funds(self, n):
+ if self.stock == 0:
+ return f'Nothing left to vend. Please restock. Here is your ${n}.'
+ # Alternatively, we could have:
+ # return self.vend() + f' Here is your ${n}.'
+ self.balance += n
+ return f'Current balance: ${self.balance}'
+
+ def vend(self):
+ if self.stock == 0:
+ return 'Nothing left to vend. Please restock.'
+ difference = self.price - self.balance
+ if difference > 0:
+ return f'Please add ${difference} more funds.'
+ message = f'Here is your {self.product}'
+ if difference != 0:
+ message += f' and ${-difference} change'
+ self.balance = 0
+ self.stock -= 1
+ return message + '.'
+
+
+
+
Use Ok to test your code:
python3 ok -q VendingMachine
+
+
+
+
+
+
Reading through the doctests, it should be clear which functions we should add
+to ensure that the vending machine class behaves correctly.
+
+
__init__
+
+
+
This can be difficult to fill out at first. Both product and price seem
+ pretty obvious to keep around, but stock and balance are quantities that
+ are needed only after attempting other functions.
+
+
+
restock
+
+
+
Even though v.restock(2) takes only one argument in the doctest, remember
+ that self is bound to the object the restock method is invoked on.
+ Therefore, this function has two parameters.
+
While implementing this function, you will probably realize that you would
+ like to keep track of the stock somewhere. While it might be possible to set
+ the stock in this function as an instance attribute, it would lose whatever
+ the old stock was.
+ Therefore, the natural solution is to initialize stock in the constructor, and
+ then update it in restock.
+
+
+
add_funds
+
+
+
This behaves very similarly to restock. See comments above.
+
Also yes, this is quite the expensive vending machine.
+
+
+
vend
+
+
+
The trickiest thing here is to make sure we handle all the cases. You may find
+ it helpful when implementing a problem like this to keep track of all the
+ errors we run into in the doctest.
+
+
+
No stock
+
Not enough balance
+
Leftover balance after purchase (return change to customer)
+
No leftover balance after purchase
+
+
We use some string concatenation at the end when handling case 3 and 4 to try
+ and reduce the amount of code. This isn't necessary for correctness -- it's ok
+ to have something like:
Of course, that would require decrementing the balance and stock beforehand.
+
+
+
+
+
+
Check Your Score Locally
+
+
You can locally check your score on each question of this assignment by running
+
+
python3 ok --score
+
+
This does NOT submit the assignment! When you are satisfied with your score, submit the assignment to Gradescope to receive credit for it.
+
+
Submit Assignment
+
+
+
Submit this assignment by uploading any files you've edited to the appropriate Gradescope assignment.Lab 00 has detailed instructions.
+
+
+
Optional Questions
+
+
+
Q3: Store Digits
+
+
+
Write a function store_digits that takes in an integer n and returns
+a linked list where each element of the list is a digit of n.
+
+
Important: Do not use any string manipulation functions like str and reversed.
+
+
+
+
def store_digits(n):
+ """Stores the digits of a positive number n in a linked list.
+
+ >>> s = store_digits(1)
+ >>> s
+ Link(1)
+ >>> store_digits(2345)
+ Link(2, Link(3, Link(4, Link(5))))
+ >>> store_digits(876)
+ Link(8, Link(7, Link(6)))
+ >>> store_digits(2450)
+ Link(2, Link(4, Link(5, Link(0))))
+ >>> # a check for restricted functions
+ >>> import inspect, re
+ >>> cleaned = re.sub(r"#.*\\n", '', re.sub(r'"{3}[\s\S]*?"{3}', '', inspect.getsource(store_digits)))
+ >>> print("Do not use str or reversed!") if any([r in cleaned for r in ["str", "reversed"]]) else None
+ """
+
result = Link.empty
+ while n > 0:
+ result = Link(n % 10, result)
+ n //= 10
+ return result
+
+
+
+
Use Ok to test your code:
python3 ok -q store_digits
+
+
+
+
+
Q4: Mutable Mapping
+
+
+
Implement deep_map_mut(func, link), which applies a function func onto
+all elements in the given linked list lnk. If an element is itself a
+linked list, apply func to each of its elements, and so on.
+
+
Your implementation should mutate the original linked list. Do not
+create any new linked lists.
+
+
Hint: The built-in isinstance function may be useful.
Construct Check: The last doctest of this question ensures that you
+do not create new linked lists. If you are failing this doctest, ensure
+that you are not creating link lists by calling the constructor, i.e.
+
+
s = Link(1)
+
+
+
+
def deep_map_mut(func, lnk):
+ """Mutates a deep link lnk by replacing each item found with the
+ result of calling func on the item. Does NOT create new Links (so
+ no use of Link's constructor).
+
+ Does not return the modified Link object.
+
+ >>> link1 = Link(3, Link(Link(4), Link(5, Link(6))))
+ >>> print(link1)
+ <3 <4> 5 6>
+ >>> # Disallow the use of making new Links before calling deep_map_mut
+ >>> Link.__init__, hold = lambda *args: print("Do not create any new Links."), Link.__init__
+ >>> try:
+ ... deep_map_mut(lambda x: x * x, link1)
+ ... finally:
+ ... Link.__init__ = hold
+ >>> print(link1)
+ <9 <16> 25 36>
+ """
+
Implement a function two_list that takes in two lists and returns a linked list. The first list contains the
+values that we want to put in the linked list, and the second list contains the number of each corresponding value.
+Assume both lists are the same size and have a length of 1 or greater. Assume all elements in the second list
+are greater than 0.
+
+
+
+
def two_list(vals, counts):
+ """
+ Returns a linked list according to the two lists that were passed in. Assume
+ vals and counts are the same size. Elements in vals represent the value, and the
+ corresponding element in counts represents the number of this value desired in the
+ final linked list. Assume all elements in counts are greater than 0. Assume both
+ lists have at least one element.
+ >>> a = [1, 3]
+ >>> b = [1, 1]
+ >>> c = two_list(a, b)
+ >>> c
+ Link(1, Link(3))
+ >>> a = [1, 3, 2]
+ >>> b = [2, 2, 1]
+ >>> c = two_list(a, b)
+ >>> c
+ Link(1, Link(1, Link(3, Link(3, Link(2)))))
+ """
+
def helper(count, index):
+ if count == 0:
+ if index + 1 == len(vals):
+ return Link.empty
+ return Link(vals[index + 1], helper(counts[index + 1] - 1, index + 1))
+ return Link(vals[index], helper(count - 1, index))
+ return helper(counts[0], 0)
+
+#Iterative solution
+def two_list_iterative(vals, counts):
+ result = Link(None)
+ p = result
+ for index in range(len(vals)):
+ item = vals[index]
+ for _ in range(counts[index]):
+ p.rest = Link(item)
+ p = p.rest
+ return result.rest
+
+
+
+
Use Ok to test your code:
python3 ok -q two_list
+
+
+
+
+
Exam Practice
+
+
Homework assignments will also contain prior exam questions for you to try.
+These questions have no submission component; feel free to attempt them if you'd like some practice!
The 61A Scheme interpreter is included in each Scheme assignment. To start it,
+type python3 scheme in a terminal. To load a Scheme file called f.scm, type python3 scheme -i f.scm. To exit the Scheme interpreter, type
+(exit).
+
+
Scheme Editor
+
+
+
All Scheme assignments include a web-based editor that makes it easy to run ok
+tests and visualize environments. Type python3 editor in a terminal, and the
+editor will open in a browser window (at http://127.0.0.1:31415/). Whatever
+changes you make here will also save to the original file on your computer!
+To stop running the editor and return to the command line, type Ctrl-C in the
+terminal where you started the editor.
+
+
The Run button loads the current assignment's .scm file and opens a Scheme
+interpreter, allowing you to try evaluating different Scheme expressions.
+
+
The Test button runs all ok tests for the assignment. Click View Case for a
+failed test, then click Debug to step through its evaluation.
+
+
Recommended VS Code Extensions
+
+
+
If you choose to use VS Code as your text editor (instead of the web-based editor),
+install the vscode-scheme
+extension so that parentheses are highlighted.
+
+
Before:
+
+
+
+
After:
+
+
+
+
In addition, the 61a-bot (installation instructions) VS Code extension is available for Scheme homeworks. The bot is also integrated into ok.
+
+
+
Required Questions
+
+
+
+
+
+
+
+
Getting Started Videos
+
+
+
These videos may provide some helpful direction for tackling the coding
+problems on this assignment.
+
+
To see these videos, you should be logged into your berkeley.edu email.
Implement a procedure pow that raises a base to the power of a nonnegative integer exp. The number of recursive pow calls should grow logarithmically with respect to exp, rather than linearly. For example, (pow 2 32) should result in 5 recursive pow calls rather than 32 recursive pow calls.
+
+
Hint:
+
+
+
x2y = (xy)2
+
x2y+1 = x(xy)2
+
+
+
For example, 216 = (28)2 and 217 = 2 * (28)2.
+
+
You may use the built-in predicates even? and odd?. Also, the square procedure is defined for you.
+
+
Scheme doesn't have while or for statements, so use recursion to solve this problem.
+
+
+
+
(define (square n) (* n n))
+
+(define (pow base exp)
+
(cond ((= exp 0) 1)
+ ((even? exp) (square (pow base (/ exp 2))))
+ (else (* base (pow base (- exp 1)))))
)
+
+
+
+
+
Use Ok to test your code:
python3 ok -q pow
+
+
+
+
+
+
We avoid unnecessary pow calls by squaring the result of base^(exp/2) when exp is even.
+
+
The else clause, which is for odd values of exp, multiplies the result of base^(exp-1) by base.
+
+
When exp is even, computing base^exp requires one more call than computing base^(exp/2). When exp is odd, computing base^exp requires two more calls than computing base^((exp-1)/2).
+
+
So we have a logarithmic runtime for pow with respect to exp.
+
+
+
+
+
+
Q2: Repeatedly Cube
+
+
+
Implement repeatedly-cube, which receives a number x and cubes it n times.
+
+
Here are some examples of how repeatedly-cube should behave:
+
+
scm> (repeatedly-cube 100 1) ; 1 cubed 100 times is still 1
+1
+scm> (repeatedly-cube 2 2) ; (2^3)^3
+512
+scm> (repeatedly-cube 3 2) ; ((2^3)^3)^3
+134217728
(define (repeatedly-cube n x)
+ (if (zero? n)
+ x
+ (let
+
((y (repeatedly-cube (- n 1) x)))
(* y y y))))
+
+
+
+
+
Use Ok to test your code:
python3 ok -q repeatedly-cube
+
+
+
+
+
+
We know our solution must be recursive because Scheme handles recursion much better than it handles iteration.
+
+
The provided code returns x when n is zero. This is the correct base case for repeatedly-cube; we just need to write the recursive case.
+
+
In the recursive case, the provided code returns (* y y y), which is the cube of y. We use recursion to set y to the result of cubing xn - 1 times. Then the cube of y is the result of cubing xn times, as desired.
+
+
+
+
+
Q3: Cadr
+
+
+
Define the procedure cadr, which returns the second element of a list. Also define caddr, which returns the third element of a list.
The second element of a list s is the first element of the rest of s. So we define (cadr s) as the car of the cdr of s.
+
+
The provided cddr procedure takes a list s and returns a list that starts at the third element of s. So we define (caddr s) as the car of the cddr of s.
The 61A Scheme interpreter is included in each Scheme assignment. To start it,
+type python3 scheme in a terminal. To load a Scheme file called f.scm, type python3 scheme -i f.scm. To exit the Scheme interpreter, type
+(exit).
+
+
Scheme Editor
+
+
+
All Scheme assignments include a web-based editor that makes it easy to run ok
+tests and visualize environments. Type python3 editor in a terminal, and the
+editor will open in a browser window (at http://127.0.0.1:31415/). Whatever
+changes you make here will also save to the original file on your computer!
+To stop running the editor and return to the command line, type Ctrl-C in the
+terminal where you started the editor.
+
+
The Run button loads the current assignment's .scm file and opens a Scheme
+interpreter, allowing you to try evaluating different Scheme expressions.
+
+
The Test button runs all ok tests for the assignment. Click View Case for a
+failed test, then click Debug to step through its evaluation.
+
+
Recommended VS Code Extensions
+
+
+
If you choose to use VS Code as your text editor (instead of the web-based editor),
+install the vscode-scheme
+extension so that parentheses are highlighted.
+
+
Before:
+
+
+
+
After:
+
+
+
+
In addition, the 61a-bot (installation instructions) VS Code extension is available for Scheme homeworks. The bot is also integrated into ok.
+
+
+
Required Questions
+
+
+
+
Required Questions
+
+
+
+
+
+
+
+
Getting Started Videos
+
+
+
These videos may provide some helpful direction for tackling the coding
+problems on this assignment.
+
+
To see these videos, you should be logged into your berkeley.edu email.
Implement a procedure called ascending?, which takes a list of numbers s and
+returns True if the numbers are in non-descending order, and False
+otherwise.
+
+
A list of numbers is non-descending if each element after the first is
+greater than or equal to the previous element. For example...
+
+
+
(1 2 3 3 4) is non-descending.
+
(1 2 3 3 2) is not.
+
+
+
Hint: The built-in null? procedure returns whether its argument is nil.
+
+
+
+
Note: The question mark in ascending? is just part of the procedure name and has no special meaning in terms of Scheme syntax. It is a common practice in Scheme to name procedures with a question mark at the end if it returns a boolean value.
We approach this much like a standard Python linked list problem.
+
+
+
Base case: when s has zero or one items, it is non-descending.
+
For the recursive case, we check that the second element is greater or equal
+ to the first and that the rest is non-descending.
+
+
+
+
+
Use Ok to unlock and test your code:
python3 ok -q ascending -u
+python3 ok -q ascending
+
+
+
+
+
+
Q2: My Filter
+
+
+
Write a procedure my-filter, which takes a predicate pred and a list s, and
+returns a new list containing only elements of the list that satisfy the
+predicate. The output should contain the elements in the same order that they
+appeared in the original list.
+
+
Note: Make sure that you are not just calling the built-in filter function in Scheme - we are asking you to re-implement this!
The approach for this problem is to call pred on each element, which we can access with car.
+
+
+
If a given element satisfies pred, then it "passes" the filter and can be included in our new list.
+
If the element does not, then we simply return the recursive call because we should not include the element.
+
+
+
+
+
Use Ok to unlock and test your code:
python3 ok -q filter -u
+python3 ok -q filter
+
+
+
+
+
+
Q3: Interleave
+
+
+
Implement the function interleave, which takes two lists lst1 and lst2 as
+arguments. interleave should return a list that interleaves the elements
+of the two lists. (In other words, the resulting list should contain elements
+alternating between lst1 and lst2, starting at lst1).
+
+
If one of the input lists to interleave is shorter than the other, then
+interleave should alternate elements from both lists until one list has no
+more elements, and then the remaining elements from the longer list should be
+added to the end of the new list. If lst1 is empty, you may simply return
+lst2 and vice versa.
The base cases for both solutions (which are equivalent), follow directly from the spec. That is, if we
+run out of elements in one list, then we should simply append the remaining elements from the longer list.
+
+
The first solution constructs the interleaved list two elements at a time, by cons-ing together the first
+two elements of each list alongside the result of recursively calling interleave on the cdr's of both lists.
+
+
The second solution constructs the interleaved list one element at a time by swapping which list is passed in for lst1.
+Thus, we can then grab elements from only lst1 to construct the list.
+
+
+
+
+
Use Ok to unlock and test your code:
python3 ok -q interleave -u
+python3 ok -q interleave
+
+
+
+
+
Q4: No Repeats
+
+
+
Implement no-repeats, which takes a list of numbers s. It returns a list
+that has all of the unique elements of s in the order that they first appear,
+but no repeats.
+
+
For example, (no-repeats (list 5 4 5 4 2 2)) evaluates to (5 4 2).
+
+
Hint: You may find it helpful to use filter with a lambda procedure to
+filter out repeats. To test if two numbers a and b are not equal, use
+(not (= a b)).
For the base case, if the input list is empty, then we do nothing and return the empty list.
+
+
Otherwise, we may attempt to proceed with the intuition that removing repeats would require us to keep
+track of what elements we have already "seen". However, this would require a helper to keep track of
+seen elements. Furthermore, Scheme does not have a built-in containment predicate analog to Python's in keyword.
+
+
Thus, we realize that we can instead remove all repeats of an element while iterating through our list. The idea is that as
+we iterate through an element of the list, we simultaneously remove all other instances of that element from the rest of the list. This ensures
+that there is only one instance of that element in the list. We achieve this by applying a filter onto the rest of the list.
+
+
+
+
+
+
Use Ok to test your code:
python3 ok -q no_repeats
+
+
+
+
+
Submit Assignment
+
+
+
Submit this assignment by uploading any files you've edited to the appropriate Gradescope assignment.Lab 00 has detailed instructions.
+
+
+
Exam Practice
+
+
The following are some Scheme List exam problems from previous semesters that you may find useful as additional exam practice.
The 61A Scheme interpreter is included in each Scheme assignment. To start it,
+type python3 scheme in a terminal. To load a Scheme file called f.scm, type python3 scheme -i f.scm. To exit the Scheme interpreter, type
+(exit).
+
+
Scheme Editor
+
+
+
All Scheme assignments include a web-based editor that makes it easy to run ok
+tests and visualize environments. Type python3 editor in a terminal, and the
+editor will open in a browser window (at http://127.0.0.1:31415/). Whatever
+changes you make here will also save to the original file on your computer!
+To stop running the editor and return to the command line, type Ctrl-C in the
+terminal where you started the editor.
+
+
The Run button loads the current assignment's .scm file and opens a Scheme
+interpreter, allowing you to try evaluating different Scheme expressions.
+
+
The Test button runs all ok tests for the assignment. Click View Case for a
+failed test, then click Debug to step through its evaluation.
+
+
Recommended VS Code Extensions
+
+
+
If you choose to use VS Code as your text editor (instead of the web-based editor),
+install the vscode-scheme
+extension so that parentheses are highlighted.
+
+
Before:
+
+
+
+
After:
+
+
+
+
In addition, the 61a-bot (installation instructions) VS Code extension is available for Scheme homeworks. The bot is also integrated into ok.
+
+
+
Required Questions
+
+
+
+
+
+
+
+
+
Getting Started Videos
+
+
+
These videos may provide some helpful direction for tackling the coding
+problems on this assignment.
+
+
To see these videos, you should be logged into your berkeley.edu email.
Recall that currying transforms a multiple argument function into a series of higher-order, one argument functions. In the next set of questions, you will be creating functions that can automatically curry a function of any length using the notion that programs are data!
+
+
+
Q1: Cooking Curry
+
+
+
Implement the function curry-cook, which takes in a Scheme list formals and a quoted expression body. curry-cook should generate a program as a list which is a curried version of a lambda function. The outputted program should be a curried version of a lambda function with formal arguments equal to formals, and a function body equal to body. You may assume that all functions passed in will have more than 0 formals; otherwise, it would not be curry-able!
+
+
For example, if you wanted to curry the function (lambda (x y) (+ x y)), you would set formals equal to '(x y), the body equal to '(+ x y), and make a call to curry-cook: (curry-cook '(x y) '(+ x y)).
+
+
scm> (curry-cook '(a) 'a)
+(lambda (a) a)
+scm> (curry-cook '(x y) '(+ x y))
+(lambda (x) (lambda (y) (+ x y)))
Implement the function curry-consume, which takes in a curried lambda function curry and applies the function to a list of arguments args. You may make the following assumptions:
+
+
+
If curry is an n-curried function, then there will be at most n arguments in args.
+
If there are 0 arguments (args is an empty list), then you may assume that curry has been fully applied with relevant arguments; in this case, curry now contains a value representing the output of the lambda function. Return it.
+
+
+
Note that there can be fewer args than formals for the corresponding lambda function curry! In the case that there are fewer arguments, curry-consume should return a curried lambda function, which is the result of partially applying curry up to the number of args provdied. See the doctests below for a few examples.
+
+
scm> (define three-curry (lambda (x) (lambda (y) (lambda (z) (+ x (* y z)))) ))
+three-curry
+scm> (define eat-two (curry-consume three-curry '(1 2))) ; pass in only two arguments, return should be a one-arg lambda function!
+eat-two
+scm> eat-two
+(lambda (z) (+ x (* y z)))
+scm> (eat-two 3) ; pass in the last argument; 1 + (2 * 3)
+7
+scm> (curry-consume three-curry '(1 2 3)) ; all three arguments at once
+7
switch is a macro that takes in an expression expr and a list of pairs, options, where the first element of each pair is some value and the second element is a single expression. switch evaluates the expression contained in the list of options that corresponds to the value that expr evaluates to.
+
+
scm> (switch (+ 1 1) ((1 (print 'a))
+ (2 (print 'b)) ; (print 'b) is evaluated because (+ 1 1) evaluates to 2
+ (3 (print 'c))))
+b
+
+
+
+
switch uses another procedure called switch-to-cond in its implementation:
Your task is to define switch-to-cond, which is a procedure (not a macro) that takes a quoted switch expression and converts it into a cond expression with the same behavior. An example is shown below.
You can locally check your score on each question of this assignment by running
+
+
python3 ok --score
+
+
This does NOT submit the assignment! When you are satisfied with your score, submit the assignment to Gradescope to receive credit for it.
+
+
Submit Assignment
+
+
+
Submit this assignment by uploading any files you've edited to the appropriate Gradescope assignment.Lab 00 has detailed instructions.
+
+
+
Exam Practice
+
+
+
Homework assignments will also contain prior exam questions for you to try.
+These questions have no submission component; feel free to attempt them if you'd like some practice!
In each question below, you will define a new table based on the following
+tables.
+
+
CREATE TABLE parents AS
+ SELECT "abraham" AS parent, "barack" AS child UNION
+ SELECT "abraham" , "clinton" UNION
+ SELECT "delano" , "herbert" UNION
+ SELECT "fillmore" , "abraham" UNION
+ SELECT "fillmore" , "delano" UNION
+ SELECT "fillmore" , "grover" UNION
+ SELECT "eisenhower" , "fillmore";
+
+CREATE TABLE dogs AS
+ SELECT "abraham" AS name, "long" AS fur, 26 AS height UNION
+ SELECT "barack" , "short" , 52 UNION
+ SELECT "clinton" , "long" , 47 UNION
+ SELECT "delano" , "long" , 46 UNION
+ SELECT "eisenhower" , "short" , 35 UNION
+ SELECT "fillmore" , "curly" , 32 UNION
+ SELECT "grover" , "short" , 28 UNION
+ SELECT "herbert" , "curly" , 31;
+
+CREATE TABLE sizes AS
+ SELECT "toy" AS size, 24 AS min, 28 AS max UNION
+ SELECT "mini" , 28 , 35 UNION
+ SELECT "medium" , 35 , 45 UNION
+ SELECT "standard" , 45 , 60;
+
+
Your tables should still perform correctly even if the values in these tables
+change. For example, if you are asked to list all dogs with a name that starts
+with h, you should write:
+
+
SELECT name FROM dogs WHERE "h" <= name AND name < "i";
+
+
Instead of assuming that the dogs table has only the data above and writing
+
+
SELECT "herbert";
+
+
The former query would still be correct if the name grover were changed to
+hoover or a row was added with the name harry.
+
+
+
Q1: By Parent Height
+
+
Create a table by_parent_height that has a column of the names of all dogs that have
+a parent, ordered by the height of the parent dog from tallest parent to shortest
+parent.
+
+
-- All dogs with parents ordered by decreasing height of their parent
+CREATE TABLE by_parent_height AS
+
SELECT child FROM parents, dogs WHERE name = parent ORDER BY height desc;
+
+
For example, fillmore has a parent eisenhower with height 35, and so
+should appear before grover who has a parent fillmore with height 32.
+The names of dogs with parents of the same height should appear together in any
+order. For example, barack and clinton should both appear at the end, but
+either one can come before the other.
We need information from both the parents and the dogs table. This time, the
+only rows that make sense are the ones where a child is matched up with their
+parent. Finally, we order the result by descending height.
+
+
+
+
+
Q2: Size of Dogs
+
+
The Fédération Cynologique Internationale classifies a standard poodle as over
+45 cm and up to 60 cm. The sizes table describes this and other such
+classifications, where a dog must be over the min and less than or equal to
+the max in height to qualify as size.
+
+
Create a size_of_dogs table with two columns, one for each dog's name and
+another for its size.
+
+
-- The size of each dog
+CREATE TABLE size_of_dogs AS
+
SELECT name, size FROM dogs, sizes
+ WHERE height > min AND height <= max;
We know that at a minimum, we need information from both the dogs and sizes
+table. Finally, we filter and keep only the rows that make sense: a size that
+corresponds to the size of the dog we're currently considering.
+
+
+
+
+
Q3: Sentences
+
+
There are two pairs of siblings that have the same size. Create a table that
+contains a row with a string for each of these pairs. Each string should be a
+sentence describing the siblings by their size.
+
+
-- Filling out this helper table is optional
+CREATE TABLE siblings AS
+
SELECT a.child AS first, b.child AS second FROM parents AS a, parents AS b
+ WHERE a.parent = b.parent AND a.child < b.child;
+-- Sentences about siblings that are the same size
+CREATE TABLE sentences AS
+
SELECT "The two siblings, " || first || " and " || second || ", have the same size: " || a.size
+ FROM siblings, size_of_dogs AS a, size_of_dogs AS b
+ WHERE a.size = b.size AND a.name = first AND b.name = second;
+
+
Each sibling pair should appear only once in the output, and siblings should be
+listed in alphabetical order (e.g. "barack and clinton..." instead of
+"clinton and barack..."), as follows:
+
+
sqlite> SELECT * FROM sentences;
+The two siblings, barack and clinton, have the same size: standard
+The two siblings, abraham and grover, have the same size: toy
+
+
Hint: First, create a helper table containing each pair of siblings. This
+will make comparing the sizes of siblings when constructing the main table
+easier.
+
+
Hint: If you join a table with itself, use AS within the FROM clause to
+give each table an alias.
+
+
Hint: In order to concatenate two strings into one, use the || operator.
+
+
+
+
Use Ok to test your code:
python3 ok -q sentences
+
+
+
+
+
+
Roughly speaking, there are two tasks we need to solve here:
+
+
Figure out which dogs are siblings
+
+
A sibling is someone you share a parent with. This will probably involve the
+parents table.
+
+
It might be tempting to join this with dogs, but there isn't any extra
+information provided by a dogs table that we need at this time. Furthermore, we
+still need information on sibling for a given dog, since the parents table
+just associates each dog to a parent.
+
+
The next step, therefore, is to match all children to all other children by
+joining the parents table to itself. The only rows here that make sense are the
+rows that represent sibling relationships since they share the same parent.
+
+
Remember that we want to avoid duplicates! If dog A and B are siblings, we don't
+want both A/B and B/A to appear in the final result. We also definitely don't
+want A/A to be a sibling pair. Enforcing ordering on the sibling names ensures
+that we don't have either issue.
+
+
Construct sentences based on sibling information
+
+
After determining the siblings, constructing the sentences just requires us to
+get the size of each sibling. We could join on the dogs and sizes tables as
+we did in an earlier problem, but there's no need to redo that work. Instead,
+we'll reuse our size_of_dogs table to figure out the size of each sibling in
+each pair.
+
+
+
+
+
Q4: Low Variance
+
+
+
We want to create a table that contains the height range (difference between maximum and minimum height) of all dogs that share a fur type. However, we'll only
+consider fur types where each dog with that fur type is within 30% of the average height of all dogs with that fur type.
+
+
For example, if the average height for short-haired dogs is 10, then in order to be included in our
+output, all dogs with short hair must have a height of at most 13 and at least 7.
+
+
To achieve this, we can use MIN, MAX, and AVG.
+For this problem, we'll want to find the average height and make sure that:
+
+
+
There are no heights smaller than 0.7 of the average.
+
There are no heights greater than 1.3 of the average.
+
+
+
Your output should first include the fur type and then the height range for the fur types that meet
+this criteria.
+
+
-- Height range for each fur type where all of the heights differ by no more than 30% from the average height
+CREATE TABLE low_variance AS
+
SELECT fur, MAX(height) - MIN(height) FROM dogs GROUP BY fur
+ HAVING MIN(height) >= .7 * AVG(height) AND MAX(height) <= 1.3 * AVG(height);
Explanation: The average height of long-haired dogs is 39.7, so the low variance criterion requires the height of each long-haired dog to be between 27.8 and 51.6. However, abraham is a long-haired dog with height 26, which is outside this range. For short-haired dogs, barack falls outside the valid range (check!). Thus, neither short nor long haired dogs are included in the output. There are two curly haired dogs: fillmore with height 32 and herbert with height 31. This gives a height range of 1.
+
+
Use Ok to test your code:
python3 ok -q low_variance
+
+
+
+
+
Submit Assignment
+
+
+
Submit this assignment by uploading any files you've edited to the appropriate Gradescope assignment.Lab 00 has detailed instructions.
+
+
Make sure to submit hw10.sql to the autograder!
+
+
+
Exam Practice
+
+
+
The following are some SQL exam problems from previous semesters that you may find useful as additional exam practice.
If 80% or more students (computed as the fraction of the number of students who took Midterm 2)
+complete all three, then all students who complete this homework get an extra credit point!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hw/sol-hw11/ok b/hw/sol-hw11/ok
new file mode 100644
index 0000000000..593354ea21
Binary files /dev/null and b/hw/sol-hw11/ok differ
diff --git a/index.html b/index.html
index 438b291500..ac9310f6cf 100644
--- a/index.html
+++ b/index.html
@@ -687,6 +687,8 @@
The sequence of keys or values or key-value pairs can be accessed using
+.keys() or .values() or .items().
+
+
>>> for k in d.keys():
+... print(k)
+...
+2
+two
+(1, 1)
+>>> for v in d.values():
+... print(v)
+...
+4
+['four']
+4
+>>> for k, v in d.items():
+... print(k, v)
+...
+2 4
+two ['four']
+(1, 1) 4
+
+
You can check whether a dictionary contains a key using in:
+
+
>>> 'two' in d
+True
+>>> 4 in d
+False
+
+
A dictionary comprehension is an expression that evaluates to a new dictionary.
+
+
>>> {3*x: 3*x + 1 for x in range(2, 5)}
+{6: 7, 9: 10, 12: 13}
+
+
+
+
+
+
Q1: Dictionaries
+
+
+
Use Ok to test your knowledge with the following "What Would Python Display?" questions:
Implement divide, which takes two lists of positive integers quotients and
+divisors. It returns a dictionary whose keys are the elements of quotients.
+For each key q, its corresponding value is a list of all the elements of
+divisors that can be evenly divided by q.
+
+
Hint: The value for each key needs be a list, so list comprehension might be useful here.
+
+
+
+
def divide(quotients, divisors):
+ """Return a dictonary in which each quotient q is a key for the list of
+ divisors that it divides evenly.
+
+ >>> divide([3, 4, 5], [8, 9, 10, 11, 12])
+ {3: [9, 12], 4: [8, 12], 5: [10]}
+ >>> divide(range(1, 5), range(20, 25))
+ {1: [20, 21, 22, 23, 24], 2: [20, 22, 24], 3: [21, 24], 4: [20, 24]}
+ """
+
return {q: [d for d in divisors if d % q == 0] for q in quotients}
+
+
+
+
Use Ok to test your code:
python3 ok -q divide
+
+
+
+
+
Q3: Buying Fruit
+
+
+
Implement the buy function that takes three parameters:
+
+
+
fruits_to_buy: A list of strings representing the fruits you need to buy. At least one of each fruit must be bought.
+
prices: A dictionary where the keys are fruit names (strings) and the values are positive integers representing the cost of each fruit.
+
total_amount: An integer representing the total money available for purchasing the fruits.
+ Take a look at the docstring for more details on the input structure.
+
+
+
The function should print all possible ways to buy the required fruits so that the combined cost equals total_amount. You can only select fruits mentioned in fruits_to_buy list.
+
+
Note: You can use the display function to format the output. Call display(fruit, count) for each fruit and its corresponding quantity to generate a string showing the type and amount of fruit bought.
+
+
+
+
Hint: How can you ensure that every combination includes at least one of each fruit listed in fruits_to_buy?
+
+
+
+
def buy(fruits_to_buy, prices, total_amount):
+ """Print ways to buy some of each fruit so that the sum of prices is amount.
+
+ >>> prices = {'oranges': 4, 'apples': 3, 'bananas': 2, 'kiwis': 9}
+ >>> buy(['apples', 'oranges', 'bananas'], prices, 12) # We can only buy apple, orange, and banana, but not kiwi
+ [2 apples][1 orange][1 banana]
+ >>> buy(['apples', 'oranges', 'bananas'], prices, 16)
+ [2 apples][1 orange][3 bananas]
+ [2 apples][2 oranges][1 banana]
+ >>> buy(['apples', 'kiwis'], prices, 36)
+ [3 apples][3 kiwis]
+ [6 apples][2 kiwis]
+ [9 apples][1 kiwi]
+ """
+ def add(fruits, amount, cart):
+ if fruits == [] and amount == 0:
+ print(cart)
+ elif fruits and amount > 0:
+ fruit = fruits[0]
+
price = prices[fruit]
for k in range(1, amount // price + 1):
# Hint: The display function will help you add fruit to the cart.
+
add(fruits_to_buy, total_amount, '')
+
+def display(fruit, count):
+ """Display a count of a fruit in square brackets.
+
+ >>> display('apples', 3)
+ '[3 apples]'
+ >>> display('apples', 1)
+ '[1 apple]'
+ >>> print(display('apples', 3) + display('kiwis', 3))
+ [3 apples][3 kiwis]
+ """
+ assert count >= 1 and fruit[-1] == 's'
+ if count == 1:
+ fruit = fruit[:-1] # get rid of the plural s
+ return '[' + str(count) + ' ' + fruit + ']'
+
+
+
+
Use Ok to test your code:
python3 ok -q buy
+
+
+
+
+
Data Abstraction
+
+
+
Consult the drop-down if you need a refresher on data abstraction. It's
+okay to skip directly to the questions and refer back
+here should you get stuck.
+
+
+
+
+
+
A data abstraction is a set of functions that compose and decompose compound
+values. One function called the constructor puts together two or more parts
+into a whole (such as a rational number; also known as a fraction), and other
+functions called selectors return parts of that whole (such as the numerator
+or denominator).
+
+
def rational(n, d):
+ "Return a fraction n / d for integers n and d."
+
+def numer(r):
+ "Return the numerator of rational number r."
+
+def denom(r):
+ "Return the denominator of rational number r."
+
+
Crucially, one can use a data abstraction without knowing how these functions are
+implemented. For example, we (humans) can verify that mul_rationals is
+implemented correctly just by knowing what rational, numer, and denom do
+without knowing how they are implemented.
+
+
def mul_rationals(r1, r2):
+ "Return the rational number r1 * r2."
+ return rational(numer(r1) * numer(r2), denom(r1) * denom(r2))
+
+
However, for Python to run the program, the data abstraction requires an
+implementation. Using knowledge of the implementation crosses the abstraction barrier, which separates the part of a program that depends on the
+implementation of the data abstraction from the part that does not. A
+well-written program typically will minimize the amount of code that depends on
+the implementation so that the implementation can be changed later on without
+requiring much code to be rewritten.
+
+
When using a data abstraction that has been provided, write your program so that
+it will still be correct even if the implementation of the data abstraction
+changes.
+
+
+
+
+
Cities
+
+
+
Say we have a data abstraction for cities. A city has a name, a latitude
+coordinate, and a longitude coordinate.
+
+
Our data abstraction has one constructor:
+
+
+
make_city(name, lat, lon): Creates a city object with the given name,
+ latitude, and longitude.
+
+
+
We also have the following selectors in order to get the information for
+each city:
+
+
+
get_name(city): Returns the city's name
+
get_lat(city): Returns the city's latitude
+
get_lon(city): Returns the city's longitude
+
+
+
Here is how we would use the constructor and selectors to create cities and
+extract their information:
All of the selector and constructor functions can be found in the lab file if
+you are curious to see how they are implemented. However, the point of data
+abstraction is that, when writing a program about cities, we do not need to know
+the implementation.
+
+
+
Q4: Distance
+
+
+
We will now implement the function distance, which computes the
+distance between two city objects. Recall that the distance between two
+coordinate pairs (x1, y1) and (x2, y2) can be found by calculating
+the sqrt of (x1 - x2)**2 + (y1 - y2)**2. We have already imported
+sqrt for your convenience. Use the latitude and longitude of a city as
+its coordinates; you'll need to use the selectors to access this info!
Next, implement closer_city, a function that takes a latitude,
+longitude, and two cities, and returns the name of the city that is
+closer to the provided latitude and longitude.
+
+
You may only use the selectors and constructors introduced above and the
+distance function you just defined for this question.
+
+
Hint: How can you use your distance function to find the distance between
+the given location and each of the given cities?
+
+
+
+
def closer_city(lat, lon, city_a, city_b):
+ """
+ Returns the name of either city_a or city_b, whichever is closest to
+ coordinate (lat, lon). If the two cities are the same distance away
+ from the coordinate, consider city_b to be the closer city.
+
+ >>> berkeley = make_city('Berkeley', 37.87, 112.26)
+ >>> stanford = make_city('Stanford', 34.05, 118.25)
+ >>> closer_city(38.33, 121.44, berkeley, stanford)
+ 'Stanford'
+ >>> bucharest = make_city('Bucharest', 44.43, 26.10)
+ >>> vienna = make_city('Vienna', 48.20, 16.37)
+ >>> closer_city(41.29, 174.78, bucharest, vienna)
+ 'Bucharest'
+ """
+
Note:
+this question has no code-writing component
+(if you implemented the previous two questions correctly).
+
+
When writing functions that use a data abstraction, we should use the
+constructor(s) and selector(s) whenever possible instead of assuming the
+data abstraction's implementation.
+Relying on a data abstraction's underlying implementation is known as
+violating the abstraction barrier.
+
+
It's possible that you passed the doctests for the previous questions
+even if you violated the abstraction barrier. To check whether or not you
+did so, run the following command:
+
+
Use Ok to test your code:
python3 ok -q check_city_abstraction
+
+
+
+
The check_city_abstraction function exists only for the doctest, which swaps
+out the implementations of the original abstraction with something else, runs
+the tests from the previous two parts, then restores the original abstraction.
+
+
The nature of the abstraction barrier guarantees that changing the
+implementation of an data abstraction shouldn't affect the functionality of
+any programs that use that data abstraction, as long as the constructors and
+selectors were used properly.
+
+
If you passed the Ok tests for the previous questions but not this one,
+the fix is simple! Just replace any code that violates the abstraction
+barrier with the appropriate constructor or selector.
+
+
Make sure that your functions pass the tests with both the first and the
+second implementations of the data abstraction and that you understand why
+they should work for both before moving on.
+
+
+
Check Your Score Locally
+
+
You can locally check your score on each question of this assignment by running
+
+
python3 ok --score
+
+
This does NOT submit the assignment! When you are satisfied with your score, submit the assignment to Gradescope to receive credit for it.
+
+
+
Submit Assignment
+
+
+
If you are in a regular section of CS 61A, fill out this lab attendance and feedback form. (If you are in the mega section, you don't need to fill out the form.)
+
+
Then, submit this assignment by uploading any files you've edited to the appropriate Gradescope assignment.Lab 00 has detailed instructions.