diff --git a/disc/disc03/index.html b/disc/disc03/index.html index dbf89b7437..f02e5e6f72 100644 --- a/disc/disc03/index.html +++ b/disc/disc03/index.html @@ -2,9 +2,9 @@ - + - + @@ -12,8 +12,8 @@ - - + + - + - - - + + + @@ -37,7 +37,7 @@ -Discussion 3 | CS 61A Fall 2024 +Discussion 3 | Data C88C Fall 2024 @@ -50,8 +50,8 @@ - - CS 61A + + Data C88C @@ -60,15 +60,15 @@ - - - -
-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.

- -

double table:

- - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
firstsecondname
breakfastdinnerLa Val's
breakfastdinnerSliver
breakfastsnackLa Val's
lunchsnackLa Val's
-
- - - - - - - Your Answer - -
- Run in 61A Code - -
- - -
- Solution - -
-- 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...

- - - - -
- -
- - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/disc/sol-disc12/index.html b/disc/sol-disc12/index.html deleted file mode 100644 index 3f46ce6448..0000000000 --- a/disc/sol-disc12/index.html +++ /dev/null @@ -1,866 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Discussion 12 | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Discussion 12: Final Review - - - - - - -

-
- - - - - - -

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.

- - - - Your Answer - -
- Run in 61A Code - -
- - -
- Solution - -
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.

- - - - Your Answer - -
- Run in 61A Code - -
- - -
- Solution - -
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.

- - - - Your Answer - -
- Run in 61A Code - -
- - -
- Solution - -
    (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: - -
    -
  1. Describe the input rows using FROM and WHERE clauses.
  2. -
  3. Group those rows and determine which groups should appear as output rows using GROUP BY and HAVING clauses.
  4. -
  5. Format and order the output rows and columns using SELECT and ORDER BY clauses.
  6. -
- -

SELECT (Step 3) FROM (Step 1) WHERE (Step 1) GROUP BY (Step 2) HAVING (Step 2) ORDER BY (Step 3);

- -

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.

- - - - Your Answer - -
- Run in 61A Code - -
- - -
- Solution - -
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.

- - - - Your Answer - -
- Run in 61A Code - -
- - -
- Solution - -
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/sol-hw01/construct_check.py b/hw/sol-hw01/construct_check.py deleted file mode 100644 index bee096e7e4..0000000000 --- a/hw/sol-hw01/construct_check.py +++ /dev/null @@ -1,180 +0,0 @@ -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-hw01/hw01.ok b/hw/sol-hw01/hw01.ok deleted file mode 100644 index 5ccfd9042e..0000000000 --- a/hw/sol-hw01/hw01.ok +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "Homework 1", - "endpoint": "cal/cs61a/fa24/hw01", - "src": [ - "hw01.py" - ], - "tests": { - "hw*.py": "doctest", - "tests/*.py": "ok_test" - }, - "default_tests": [ - "a_plus_abs_b", - "a_plus_abs_b_syntax_check", - "two_of_three", - "two_of_three_syntax_check", - "largest_factor", - "hailstone" - ], - "protocols": [ - "file_contents", - "grading", - "analytics", - "help", - "backup" - ] -} \ No newline at end of file diff --git a/hw/sol-hw01/hw01.py b/hw/sol-hw01/hw01.py deleted file mode 100644 index 452d09427e..0000000000 --- a/hw/sol-hw01/hw01.py +++ /dev/null @@ -1,107 +0,0 @@ -from operator import add, sub - -def a_plus_abs_b(a, b): - """Return a+abs(b), but without calling abs. - - >>> a_plus_abs_b(2, 3) - 5 - >>> a_plus_abs_b(2, -3) - 5 - >>> a_plus_abs_b(-1, 4) - 3 - >>> a_plus_abs_b(-1, -4) - 3 - """ - if b < 0: - f = sub - else: - f = add - return f(a, b) - -def a_plus_abs_b_syntax_check(): - """Check that you didn't change the return statement of a_plus_abs_b. - - >>> # You aren't expected to understand the code of this test. - >>> import inspect, re - >>> re.findall(r'^\s*(return .*)', inspect.getsource(a_plus_abs_b), re.M) - ['return f(a, b)'] - """ - # You don't need to edit this function. It's just here to check your work. - - -def two_of_three(i, j, k): - """Return m*m + n*n, where m and n are the two smallest members of the - positive numbers i, j, and k. - - >>> two_of_three(1, 2, 3) - 5 - >>> two_of_three(5, 3, 1) - 10 - >>> two_of_three(10, 2, 8) - 68 - >>> two_of_three(5, 5, 5) - 50 - """ - return min(i*i+j*j, i*i+k*k, j*j+k*k) - # Alternate solution -def two_of_three_alternate(i, j, k): - return i**2 + j**2 + k**2 - max(i, j, k)**2 - -def two_of_three_syntax_check(): - """Check that your two_of_three code consists of nothing but a return statement. - - >>> # You aren't expected to understand the code of this test. - >>> import inspect, ast - >>> [type(x).__name__ for x in ast.parse(inspect.getsource(two_of_three)).body[0].body] - ['Expr', 'Return'] - """ - # You don't need to edit this function. It's just here to check your work. - - -def largest_factor(n): - """Return the largest factor of n that is smaller than n. - - >>> largest_factor(15) # factors are 1, 3, 5 - 5 - >>> largest_factor(80) # factors are 1, 2, 4, 5, 8, 10, 16, 20, 40 - 40 - >>> largest_factor(13) # factor is 1 since 13 is prime - 1 - """ - factor = n - 1 - while factor > 0: - if n % factor == 0: - return factor - factor -= 1 - - -def hailstone(n): - """Print the hailstone sequence starting at n and return its - length. - - >>> a = hailstone(10) - 10 - 5 - 16 - 8 - 4 - 2 - 1 - >>> a - 7 - >>> b = hailstone(1) - 1 - >>> b - 1 - """ - length = 1 - while n != 1: - print(n) - if n % 2 == 0: - n = n // 2 # Integer division prevents "1.0" output - else: - n = 3 * n + 1 - length = length + 1 - print(n) # n is now 1 - return length - diff --git a/hw/sol-hw01/hw01.zip b/hw/sol-hw01/hw01.zip deleted file mode 100644 index 9b13df9420..0000000000 Binary files a/hw/sol-hw01/hw01.zip and /dev/null differ diff --git a/hw/sol-hw01/index.html b/hw/sol-hw01/index.html deleted file mode 100644 index 85c6b249d0..0000000000 --- a/hw/sol-hw01/index.html +++ /dev/null @@ -1,544 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Homework 1 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Homework 1 Solutions - - - - - - -

-
- -

Solution Files

-

You can find the solutions in hw01.py.

- - -
- - -
- -

Getting Started Videos

- - -

These videos may provide some helpful direction for tackling the -problems on this assignment.

- -

To see these videos, you should be logged into your berkeley.edu email. - -

YouTube link

-

- - -

Required Questions

- - - -

Q1: A Plus Abs B

- - -

Python's operator module contains two-argument functions such as add and -sub for Python's built-in arithmetic operators. For example, add(2, 3) -evalutes to 5, just like the expression 2 + 3.

- -

Fill in the blanks in the following function for adding a to the -absolute value of b, without calling abs. You may not modify any -of the provided code other than the two blanks.

- -
def a_plus_abs_b(a, b):
-    """Return a+abs(b), but without calling abs.
-
-    >>> a_plus_abs_b(2, 3)
-    5
-    >>> a_plus_abs_b(2, -3)
-    5
-    >>> a_plus_abs_b(-1, 4)
-    3
-    >>> a_plus_abs_b(-1, -4)
-    3
-    """
-    if b < 0:
-
f = sub
else: -
f = add
return f(a, b)
- -

Use Ok to test your code:

python3 ok -q a_plus_abs_b
- -
- -

Use Ok to run the local syntax checker (which checks that you didn't modify any of the provided code other than the two blanks):

- -
python3 ok -q a_plus_abs_b_syntax_check
- - - -
- -

If b is positive, we add the numbers together. If b is negative, we -subtract the numbers. Therefore, we choose the operator add or sub -based on the sign of b.

- -
- - -

Q2: Two of Three

- - -

Write a function that takes three positive numbers as arguments and returns -the sum of the squares of the two smallest numbers. Use only a single line -for the body of the function.

- -
def two_of_three(i, j, k):
-    """Return m*m + n*n, where m and n are the two smallest members of the
-    positive numbers i, j, and k.
-
-    >>> two_of_three(1, 2, 3)
-    5
-    >>> two_of_three(5, 3, 1)
-    10
-    >>> two_of_three(10, 2, 8)
-    68
-    >>> two_of_three(5, 5, 5)
-    50
-    """
-
return min(i*i+j*j, i*i+k*k, j*j+k*k) - # Alternate solution -def two_of_three_alternate(i, j, k): - return i**2 + j**2 + k**2 - max(i, j, k)**2
- -

Hint: Consider using the max or min function:

- -
>>> max(1, 2, 3)
-3
->>> min(-1, -2, -3)
--3
- -

Use Ok to test your code:

python3 ok -q two_of_three
- -
- -

Use Ok to run the local syntax checker (which checks that you used only a single line for the body of the function):

- -
python3 ok -q two_of_three_syntax_check
- - - -
- -

We use the fact that if x>y and y>0, then square(x)>square(y). -So, we can take the min of the sum of squares of all pairs. The -min function can take an arbitrary number of arguments.

- -

Alternatively, we can do the sum of squares of all the numbers. Then we -pick the largest value, and subtract the square of that.

- -
- - -

Q3: Largest Factor

- - -

Write a function that takes an integer n that is greater than 1 and -returns the largest integer that is smaller than n and evenly divides n.

- -
def largest_factor(n):
-    """Return the largest factor of n that is smaller than n.
-
-    >>> largest_factor(15) # factors are 1, 3, 5
-    5
-    >>> largest_factor(80) # factors are 1, 2, 4, 5, 8, 10, 16, 20, 40
-    40
-    >>> largest_factor(13) # factor is 1 since 13 is prime
-    1
-    """
-
factor = n - 1 - while factor > 0: - if n % factor == 0: - return factor - factor -= 1
- -

Hint: To check if b evenly divides a, -use the expression a % b == 0, which can be read as, -"the remainder when dividing a by b is 0."

- -

Use Ok to test your code:

python3 ok -q largest_factor
- -
- -
- -

Iterating from n-1 to 1, we return the first integer that evenly divides -n. This is guaranteed to be the largest factor of n.

- -
- - -

Q4: Hailstone

- - -

Douglas Hofstadter's Pulitzer-prize-winning book, Gödel, Escher, Bach, poses -the following mathematical puzzle.

- -
    -
  1. Pick a positive integer n as the start.
  2. -
  3. If n is even, divide it by 2.
  4. -
  5. If n is odd, multiply it by 3 and add 1.
  6. -
  7. Continue this process until n is 1.
  8. -
- -

The number n will travel up and down but eventually end at 1 (at least for -all numbers that have ever been tried -- nobody has ever proved that the -sequence will terminate). Analogously, a hailstone travels up and down in the -atmosphere before eventually landing on earth.

- -

This sequence of values of n is often called a Hailstone sequence. Write a -function that takes a single argument with formal parameter name n, prints -out the hailstone sequence starting at n, and returns the number of steps in -the sequence:

- -
def hailstone(n):
-    """Print the hailstone sequence starting at n and return its
-    length.
-
-    >>> a = hailstone(10)
-    10
-    5
-    16
-    8
-    4
-    2
-    1
-    >>> a
-    7
-    >>> b = hailstone(1)
-    1
-    >>> b
-    1
-    """
-
length = 1 - while n != 1: - print(n) - if n % 2 == 0: - n = n // 2 # Integer division prevents "1.0" output - else: - n = 3 * n + 1 - length = length + 1 - print(n) # n is now 1 - return length
- -

Hailstone sequences can get quite long! Try 27. What's the longest you can -find?

- -

Note that if n == 1 initially, then the sequence is one step long.
-Hint: If you see 4.0 but want just 4, try using floor division // instead of regular division /.

- -

Use Ok to test your code:

python3 ok -q hailstone
- -
- -

Curious about hailstone sequences? Take a look at this article:

- -
    -
  • In 2019, there was a major development in understanding how the hailstone conjecture works for most numbers!
  • -
- -
- -

We keep track of the current length of the hailstone sequence and the current -value of the hailstone sequence. From there, we loop until we hit the end of -the sequence, updating the length in each step.

- -

Note: we need to do floor division // to remove decimals.

- -
- - -

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.

- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/hw/sol-hw01/ok b/hw/sol-hw01/ok deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/hw/sol-hw01/ok and /dev/null differ diff --git a/hw/sol-hw02/construct_check.py b/hw/sol-hw02/construct_check.py deleted file mode 100644 index bee096e7e4..0000000000 --- a/hw/sol-hw02/construct_check.py +++ /dev/null @@ -1,180 +0,0 @@ -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-hw02/hw02.ok b/hw/sol-hw02/hw02.ok deleted file mode 100644 index b51c644463..0000000000 --- a/hw/sol-hw02/hw02.ok +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "Homework 2", - "endpoint": "cal/cs61a/fa24/hw02", - "src": [ - "hw02.py" - ], - "tests": { - "hw*.py": "doctest", - "tests/*.py": "ok_test" - }, - "default_tests": [ - "product", - "accumulate", - "summation_using_accumulate", - "product_using_accumulate", - "make_repeater" - ], - "protocols": [ - "restore", - "file_contents", - "grading", - "help", - "analytics", - "backup" - ] -} \ No newline at end of file diff --git a/hw/sol-hw02/hw02.py b/hw/sol-hw02/hw02.py deleted file mode 100644 index e0d981fe61..0000000000 --- a/hw/sol-hw02/hw02.py +++ /dev/null @@ -1,121 +0,0 @@ -from operator import add, mul - -square = lambda x: x * x - -identity = lambda x: x - -triple = lambda x: 3 * x - -increment = lambda x: x + 1 - - -HW_SOURCE_FILE=__file__ - - -def product(n, term): - """Return the product of the first n terms in a sequence. - - n: a positive integer - term: a function that takes one argument to produce the term - - >>> product(3, identity) # 1 * 2 * 3 - 6 - >>> product(5, identity) # 1 * 2 * 3 * 4 * 5 - 120 - >>> product(3, square) # 1^2 * 2^2 * 3^2 - 36 - >>> product(5, square) # 1^2 * 2^2 * 3^2 * 4^2 * 5^2 - 14400 - >>> product(3, increment) # (1+1) * (2+1) * (3+1) - 24 - >>> product(3, triple) # 1*3 * 2*3 * 3*3 - 162 - """ - prod, k = 1, 1 - while k <= n: - prod, k = term(k) * prod, k + 1 - return prod - - -def accumulate(fuse, start, n, term): - """Return the result of fusing together the first n terms in a sequence - and start. The terms to be fused are term(1), term(2), ..., term(n). - The function fuse is a two-argument commutative & associative function. - - >>> accumulate(add, 0, 5, identity) # 0 + 1 + 2 + 3 + 4 + 5 - 15 - >>> accumulate(add, 11, 5, identity) # 11 + 1 + 2 + 3 + 4 + 5 - 26 - >>> accumulate(add, 11, 0, identity) # 11 (fuse is never used) - 11 - >>> accumulate(add, 11, 3, square) # 11 + 1^2 + 2^2 + 3^2 - 25 - >>> accumulate(mul, 2, 3, square) # 2 * 1^2 * 2^2 * 3^2 - 72 - >>> # 2 + (1^2 + 1) + (2^2 + 1) + (3^2 + 1) - >>> accumulate(lambda x, y: x + y + 1, 2, 3, square) - 19 - """ - total, k = start, 1 - while k <= n: - total, k = fuse(total, term(k)), k + 1 - return total - -# Alternative solution -def accumulate_reverse(fuse, start, n, term): - total, k = start, n - while k >= 1: - total, k = fuse(total, term(k)), k - 1 - return total - - -def summation_using_accumulate(n, term): - """Returns the sum: term(1) + ... + term(n), using accumulate. - - >>> summation_using_accumulate(5, square) # square(0) + square(1) + ... + square(4) + square(5) - 55 - >>> summation_using_accumulate(5, triple) # triple(0) + triple(1) + ... + triple(4) + triple(5) - 45 - >>> # This test checks that the body of the function is just a return statement. - >>> import inspect, ast - >>> [type(x).__name__ for x in ast.parse(inspect.getsource(summation_using_accumulate)).body[0].body] - ['Expr', 'Return'] - """ - return accumulate(add, 0, n, term) - - -def product_using_accumulate(n, term): - """Returns the product: term(1) * ... * term(n), using accumulate. - - >>> product_using_accumulate(4, square) # square(1) * square(2) * square(3) * square() - 576 - >>> product_using_accumulate(6, triple) # triple(1) * triple(2) * ... * triple(5) * triple(6) - 524880 - >>> # This test checks that the body of the function is just a return statement. - >>> import inspect, ast - >>> [type(x).__name__ for x in ast.parse(inspect.getsource(product_using_accumulate)).body[0].body] - ['Expr', 'Return'] - """ - return accumulate(mul, 1, n, term) - - -def make_repeater(f, n): - """Returns the function that computes the nth application of f. - - >>> add_three = make_repeater(increment, 3) - >>> add_three(5) - 8 - >>> make_repeater(triple, 5)(1) # 3 * (3 * (3 * (3 * (3 * 1)))) - 243 - >>> make_repeater(square, 2)(5) # square(square(5)) - 625 - >>> make_repeater(square, 3)(5) # square(square(square(5))) - 390625 - """ - def repeater(x): - k = 0 - while k < n: - x, k = f(x), k + 1 - return x - return repeater - diff --git a/hw/sol-hw02/hw02.zip b/hw/sol-hw02/hw02.zip deleted file mode 100644 index 79a5a3818a..0000000000 Binary files a/hw/sol-hw02/hw02.zip and /dev/null differ diff --git a/hw/sol-hw02/index.html b/hw/sol-hw02/index.html deleted file mode 100644 index 359e0a5f48..0000000000 --- a/hw/sol-hw02/index.html +++ /dev/null @@ -1,557 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Homework 2 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Homework 2 Solutions - - - - - - -

-
- -

Solution Files

-

You can find solutions for all questions in hw02.py.

- - -

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.

- - -

YouTube link

-
- -

Several doctests refer to these functions:

- -
from operator import add, mul
-
-square = lambda x: x * x
-
-identity = lambda x: x
-
-triple = lambda x: 3 * x
-
-increment = lambda x: x + 1
- -
- - -

Higher-Order Functions

- - - -

Q1: Product

- - -

Write a function called product that returns the product of the first n terms of a sequence. -Specifically, product takes in an integer n and term, a single-argument function that determines a sequence. -(That is, term(i) gives the ith term of the sequence.) -product(n, term) should return term(1) * ... * term(n).

- -
def product(n, term):
-    """Return the product of the first n terms in a sequence.
-
-    n: a positive integer
-    term:  a function that takes one argument to produce the term
-
-    >>> product(3, identity)  # 1 * 2 * 3
-    6
-    >>> product(5, identity)  # 1 * 2 * 3 * 4 * 5
-    120
-    >>> product(3, square)    # 1^2 * 2^2 * 3^2
-    36
-    >>> product(5, square)    # 1^2 * 2^2 * 3^2 * 4^2 * 5^2
-    14400
-    >>> product(3, increment) # (1+1) * (2+1) * (3+1)
-    24
-    >>> product(3, triple)    # 1*3 * 2*3 * 3*3
-    162
-    """
-
prod, k = 1, 1 - while k <= n: - prod, k = term(k) * prod, k + 1 - return prod
- -

Use Ok to test your code:

python3 ok -q product
- -
- -
- -

The prod variable is used to keep track of the product so far. We -start with prod = 1 since we will be multiplying, and anything multiplied -by 1 is itself. We then initialize the counter variable k to use in the while -loop to ensures that we get through all values 1 through k.

- -
- - -

Q2: Accumulate

- - -

Let's take a look at how product is an instance of a more -general function called accumulate, which we would like to implement:

- -
def accumulate(fuse, start, n, term):
-    """Return the result of fusing together the first n terms in a sequence 
-    and start.  The terms to be fused are term(1), term(2), ..., term(n). 
-    The function fuse is a two-argument commutative & associative function.
-
-    >>> accumulate(add, 0, 5, identity)  # 0 + 1 + 2 + 3 + 4 + 5
-    15
-    >>> accumulate(add, 11, 5, identity) # 11 + 1 + 2 + 3 + 4 + 5
-    26
-    >>> accumulate(add, 11, 0, identity) # 11 (fuse is never used)
-    11
-    >>> accumulate(add, 11, 3, square)   # 11 + 1^2 + 2^2 + 3^2
-    25
-    >>> accumulate(mul, 2, 3, square)    # 2 * 1^2 * 2^2 * 3^2
-    72
-    >>> # 2 + (1^2 + 1) + (2^2 + 1) + (3^2 + 1)
-    >>> accumulate(lambda x, y: x + y + 1, 2, 3, square)
-    19
-    """
-
total, k = start, 1 - while k <= n: - total, k = fuse(total, term(k)), k + 1 - return total - -# Alternative solution -def accumulate_reverse(fuse, start, n, term): - total, k = start, n - while k >= 1: - total, k = fuse(total, term(k)), k - 1 - return total
- -

accumulate has the following parameters:

- -
    -
  • fuse: a two-argument function that specifies how the current term - is fused with the previously accumulated terms
  • -
  • start: value at which to start the accumulation
  • -
  • n: a non-negative integer indicating the number of terms to fuse
  • -
  • term: a single-argument function; term(i) is the ith term of the sequence
  • -
- -

Implement accumulate, which fuses the first n terms of the sequence defined -by term with the start value using the fuse function.

- -

For example, the result of accumulate(add, 11, 3, square) is

- -
add(11,  add(square(1), add(square(2),  square(3)))) =
-    11 +     square(1) +    square(2) + square(3)    =
-    11 +     1         +    4         + 9            = 25
- -

Assume that fuse is commutative, fuse(a, b) == fuse(b, a), and -associative, fuse(fuse(a, b), c) == fuse(a, fuse(b, c)).

- -

Then, implement summation (from lecture) and product as one-line calls to -accumulate.

- -

Important: Both summation_using_accumulate and product_using_accumulate -should be implemented with a single line of code starting with return.

- -
def summation_using_accumulate(n, term):
-    """Returns the sum: term(1) + ... + term(n), using accumulate.
-
-    >>> summation_using_accumulate(5, square) # square(0) + square(1) + ... + square(4) + square(5)
-    55
-    >>> summation_using_accumulate(5, triple) # triple(0) + triple(1) + ... + triple(4) + triple(5)
-    45
-    >>> # This test checks that the body of the function is just a return statement.
-    >>> import inspect, ast
-    >>> [type(x).__name__ for x in ast.parse(inspect.getsource(summation_using_accumulate)).body[0].body]
-    ['Expr', 'Return']
-    """
-
return accumulate(add, 0, n, term)
-def product_using_accumulate(n, term): - """Returns the product: term(1) * ... * term(n), using accumulate. - - >>> product_using_accumulate(4, square) # square(1) * square(2) * square(3) * square() - 576 - >>> product_using_accumulate(6, triple) # triple(1) * triple(2) * ... * triple(5) * triple(6) - 524880 - >>> # This test checks that the body of the function is just a return statement. - >>> import inspect, ast - >>> [type(x).__name__ for x in ast.parse(inspect.getsource(product_using_accumulate)).body[0].body] - ['Expr', 'Return'] - """ -
return accumulate(mul, 1, n, term)
- -

Use Ok to test your code:

python3 ok -q accumulate
-python3 ok -q summation_using_accumulate
-python3 ok -q product_using_accumulate
- -
- -
- -

We want to abstract the logic of product and summation into accumulate. -The differences between product and summation are:

- -
    -
  • How to fuse terms. For product, we fuse via * (mul). - For summation, we fuse via + (add).
  • -
  • The starting value. For product, we want to start off with 1 since - starting with 0 means that our result (via multiplying with the start) - will always be 0. For summation, we want to start off with 0. -
- - - -

Q3: Make Repeater

- - -

Implement the function make_repeater which takes a one-argument function f -and a positive integer n. It returns a one-argument function, where -make_repeater(f, n)(x) returns the value of f(f(...f(x)...)) in which f is -applied n times to x. For example, make_repeater(square, 3)(5) squares 5 -three times and returns 390625, just like square(square(square(5))).

- -
def make_repeater(f, n):
-    """Returns the function that computes the nth application of f.
-
-    >>> add_three = make_repeater(increment, 3)
-    >>> add_three(5)
-    8
-    >>> make_repeater(triple, 5)(1) # 3 * (3 * (3 * (3 * (3 * 1))))
-    243
-    >>> make_repeater(square, 2)(5) # square(square(5))
-    625
-    >>> make_repeater(square, 3)(5) # square(square(square(5)))
-    390625
-    """
-
def repeater(x): - k = 0 - while k < n: - x, k = f(x), k + 1 - return x - return repeater
- -

Use Ok to test your code:

python3 ok -q make_repeater
- -
- -
- -

There are many correct ways to implement make_repeater. This solution -repeatedly applies h.

- -
- - -

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] Exam Practice

- - -

Here are some related questions from past exams for you to try. These are optional. -There is no way to submit them.

- -
    -
  1. Fall 2019 MT1 Q3: You Again [Higher-Order Functions]
  2. -
  3. Fall 2021 MT1 Q1b: tik [Functions and Expressions]
  4. -
- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/hw/sol-hw02/ok b/hw/sol-hw02/ok deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/hw/sol-hw02/ok and /dev/null differ diff --git a/hw/sol-hw03/construct_check.py b/hw/sol-hw03/construct_check.py deleted file mode 100644 index bee096e7e4..0000000000 --- a/hw/sol-hw03/construct_check.py +++ /dev/null @@ -1,180 +0,0 @@ -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-hw03/hw03.ok b/hw/sol-hw03/hw03.ok deleted file mode 100644 index fde2845aad..0000000000 --- a/hw/sol-hw03/hw03.ok +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "Homework 3", - "endpoint": "cal/cs61a/fa24/hw03", - "src": [ - "hw03.py" - ], - "tests": { - "hw*.py": "doctest", - "tests/*.py": "ok_test" - }, - "default_tests": [ - "num_eights", - "digit_distance", - "interleaved_sum", - "count_coins" - ], - "protocols": [ - "restore", - "file_contents", - "analytics", - "help", - "unlock", - "grading", - "backup" - ] -} \ No newline at end of file diff --git a/hw/sol-hw03/hw03.py b/hw/sol-hw03/hw03.py deleted file mode 100644 index c39769d928..0000000000 --- a/hw/sol-hw03/hw03.py +++ /dev/null @@ -1,247 +0,0 @@ -LAB_SOURCE_FILE=__file__ - - -HW_SOURCE_FILE=__file__ - - -def num_eights(n): - """Returns the number of times 8 appears as a digit of n. - - >>> num_eights(3) - 0 - >>> num_eights(8) - 1 - >>> num_eights(88888888) - 8 - >>> num_eights(2638) - 1 - >>> num_eights(86380) - 2 - >>> num_eights(12345) - 0 - >>> num_eights(8782089) - 3 - >>> from construct_check import check - >>> # ban all assignment statements - >>> check(HW_SOURCE_FILE, 'num_eights', - ... ['Assign', 'AnnAssign', 'AugAssign', 'NamedExpr', 'For', 'While']) - True - """ - if n % 10 == 8: - return 1 + num_eights(n // 10) - elif n < 10: - return 0 - else: - return num_eights(n // 10) - - -def digit_distance(n): - """Determines the digit distance of n. - - >>> digit_distance(3) - 0 - >>> digit_distance(777) - 0 - >>> digit_distance(314) - 5 - >>> digit_distance(31415926535) - 32 - >>> digit_distance(3464660003) - 16 - >>> from construct_check import check - >>> # ban all loops - >>> check(HW_SOURCE_FILE, 'digit_distance', - ... ['For', 'While']) - True - """ - if n < 10: - return 0 - return abs(n % 10 - (n // 10) % 10) + digit_distance(n // 10) - -# Alternate solution 1 -def digit_distance_alt(n): - def helper(prev, n): - if n == 0: - return 0 - dist = abs(prev - n % 10) - return dist + helper(n % 10, n // 10) - return helper(n % 10, n // 10) - -# Alternate solution 2 -def digit_distance_alt_2(n): - def helper(dist, prev, n): - if n == 0: - return dist - dist += abs(prev - n % 10) - prev = n % 10 - n //= 10 - return helper(dist, prev, n) - return helper(0, n % 10, n // 10) - - -def interleaved_sum(n, odd_func, even_func): - """Compute the sum odd_func(1) + even_func(2) + odd_func(3) + ..., up - to n. - - >>> identity = lambda x: x - >>> square = lambda x: x * x - >>> triple = lambda x: x * 3 - >>> interleaved_sum(5, identity, square) # 1 + 2*2 + 3 + 4*4 + 5 - 29 - >>> interleaved_sum(5, square, identity) # 1*1 + 2 + 3*3 + 4 + 5*5 - 41 - >>> interleaved_sum(4, triple, square) # 1*3 + 2*2 + 3*3 + 4*4 - 32 - >>> interleaved_sum(4, square, triple) # 1*1 + 2*3 + 3*3 + 4*3 - 28 - >>> from construct_check import check - >>> check(HW_SOURCE_FILE, 'interleaved_sum', ['While', 'For', 'Mod']) # ban loops and % - True - >>> check(HW_SOURCE_FILE, 'interleaved_sum', ['BitAnd', 'BitOr', 'BitXor']) # ban bitwise operators, don't worry about these if you don't know what they are - True - """ - def sum_from(k): - if k > n: - return 0 - elif k == n: - return odd_func(k) - else: - return odd_func(k) + even_func(k+1) + sum_from(k + 2) - return sum_from(1) - - -def next_larger_coin(coin): - """Returns the next larger coin in order. - >>> next_larger_coin(1) - 5 - >>> next_larger_coin(5) - 10 - >>> next_larger_coin(10) - 25 - >>> next_larger_coin(2) # Other values return None - """ - if coin == 1: - return 5 - elif coin == 5: - return 10 - elif coin == 10: - return 25 - -def next_smaller_coin(coin): - """Returns the next smaller coin in order. - >>> next_smaller_coin(25) - 10 - >>> next_smaller_coin(10) - 5 - >>> next_smaller_coin(5) - 1 - >>> next_smaller_coin(2) # Other values return None - """ - if coin == 25: - return 10 - elif coin == 10: - return 5 - elif coin == 5: - return 1 - -def count_coins(total): - """Return the number of ways to make change using coins of value of 1, 5, 10, 25. - >>> count_coins(15) - 6 - >>> count_coins(10) - 4 - >>> count_coins(20) - 9 - >>> count_coins(100) # How many ways to make change for a dollar? - 242 - >>> count_coins(200) - 1463 - >>> from construct_check import check - >>> # ban iteration - >>> check(HW_SOURCE_FILE, 'count_coins', ['While', 'For']) - True - """ - def constrained_count(total, smallest_coin): - if total == 0: - return 1 - if total < 0: - return 0 - if smallest_coin == None: - return 0 - without_coin = constrained_count(total, next_larger_coin(smallest_coin)) - with_coin = constrained_count(total - smallest_coin, smallest_coin) - return without_coin + with_coin - return constrained_count(total, 1) - - # Alternate solution: using next_smaller_coin - def constrained_count_small(total, largest_coin): - if total == 0: - return 1 - if total < 0: - return 0 - if largest_coin == None: - return 0 - without_coin = constrained_count_small(total, next_smaller_coin(largest_coin)) - with_coin = constrained_count_small(total - largest_coin, largest_coin) - return without_coin + with_coin - return constrained_count_small(total, 25) - - -def print_move(origin, destination): - """Print instructions to move a disk.""" - print("Move the top disk from rod", origin, "to rod", destination) - -def move_stack(n, start, end): - """Print the moves required to move n disks on the start pole to the end - pole without violating the rules of Towers of Hanoi. - - n -- number of disks - start -- a pole position, either 1, 2, or 3 - end -- a pole position, either 1, 2, or 3 - - There are exactly three poles, and start and end must be different. Assume - that the start pole has at least n disks of increasing size, and the end - pole is either empty or has a top disk larger than the top n start disks. - - >>> move_stack(1, 1, 3) - Move the top disk from rod 1 to rod 3 - >>> move_stack(2, 1, 3) - Move the top disk from rod 1 to rod 2 - Move the top disk from rod 1 to rod 3 - Move the top disk from rod 2 to rod 3 - >>> move_stack(3, 1, 3) - Move the top disk from rod 1 to rod 3 - Move the top disk from rod 1 to rod 2 - Move the top disk from rod 3 to rod 2 - Move the top disk from rod 1 to rod 3 - Move the top disk from rod 2 to rod 1 - Move the top disk from rod 2 to rod 3 - Move the top disk from rod 1 to rod 3 - """ - assert 1 <= start <= 3 and 1 <= end <= 3 and start != end, "Bad start/end" - if n == 1: - print_move(start, end) - else: - other = 6 - start - end - move_stack(n-1, start, other) - print_move(start, end) - move_stack(n-1, other, end) - - -from operator import sub, mul - -def make_anonymous_factorial(): - """Return the value of an expression that computes factorial. - - >>> make_anonymous_factorial()(5) - 120 - >>> from construct_check import check - >>> # ban any assignments or recursion - >>> check(HW_SOURCE_FILE, 'make_anonymous_factorial', - ... ['Assign', 'AnnAssign', 'AugAssign', 'NamedExpr', 'FunctionDef', 'Recursion']) - True - """ - return (lambda f: lambda k: f(f, k))(lambda f, k: k if k == 1 else mul(k, f(f, sub(k, 1)))) - # Alternate solution: - return (lambda f: f(f))(lambda f: lambda x: 1 if x == 0 else x * f(f)(x - 1)) - diff --git a/hw/sol-hw03/hw03.zip b/hw/sol-hw03/hw03.zip deleted file mode 100644 index 514d386032..0000000000 Binary files a/hw/sol-hw03/hw03.zip and /dev/null differ diff --git a/hw/sol-hw03/index.html b/hw/sol-hw03/index.html deleted file mode 100644 index 472f2b63ad..0000000000 --- a/hw/sol-hw03/index.html +++ /dev/null @@ -1,936 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Homework 3 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Homework 3 Solutions - - - - - - -

-
- -

Solution Files

-

You can find the solutions in hw03.py.

- - -

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.

- - -

YouTube link

-
- - -

Q1: Num Eights

- - -

Write a recursive function num_eights that takes a positive integer n and -returns the number of times the digit 8 appears in n.

- -

Important: -Use recursion; the tests will fail if you use any assignment statements or loops. -(You can, however, use function definitions if you'd like.)

- - - -
def num_eights(n):
-    """Returns the number of times 8 appears as a digit of n.
-
-    >>> num_eights(3)
-    0
-    >>> num_eights(8)
-    1
-    >>> num_eights(88888888)
-    8
-    >>> num_eights(2638)
-    1
-    >>> num_eights(86380)
-    2
-    >>> num_eights(12345)
-    0
-    >>> num_eights(8782089)
-    3
-    >>> from construct_check import check
-    >>> # ban all assignment statements
-    >>> check(HW_SOURCE_FILE, 'num_eights',
-    ...       ['Assign', 'AnnAssign', 'AugAssign', 'NamedExpr', 'For', 'While'])
-    True
-    """
-
if n % 10 == 8: - return 1 + num_eights(n // 10) - elif n < 10: - return 0 - else: - return num_eights(n // 10)
- -
- -

Use Ok to test your code:

python3 ok -q num_eights
- -
- -
- -

The equivalent iterative version of this problem might look something like -this:

- -
total = 0
-while n > 0:
-    if n % 10 == 8:
-        total = total + 1
-    n = n // 10
-return total
- -

The main idea is that we check each digit for a eight. The recursive solution -is similar, except that you depend on the recursive call to count the -occurences of eight in the rest of the number. Then, you add that to the number -of eights you see in the current digit.

- -
- - - - - - -

Q2: Digit Distance

- - -

For a given integer, the digit distance is the sum of the absolute differences between -consecutive digits. For example:

- -
    -
  • The digit distance of 6 is 0.
  • -
  • The digit distance of 61 is 5, as the absolute value of 6 - 1 is 5.
  • -
  • The digit distance of 71253 is 12 (6 + 1 + 3 + 2).
  • -
- -

Write a function that determines the digit distance of a given positive integer. -You must use recursion or the tests will fail.

- -

Hint: There are multiple valid ways of solving this problem! -If you're stuck, try writing out an iterative solution -first, and then convert your iterative solution into a recursive one.

- - - -
def digit_distance(n):
-    """Determines the digit distance of n.
-
-    >>> digit_distance(3)
-    0
-    >>> digit_distance(777)
-    0
-    >>> digit_distance(314)
-    5
-    >>> digit_distance(31415926535)
-    32
-    >>> digit_distance(3464660003)
-    16
-    >>> from construct_check import check
-    >>> # ban all loops
-    >>> check(HW_SOURCE_FILE, 'digit_distance',
-    ...       ['For', 'While'])
-    True
-    """
-
if n < 10: - return 0 - return abs(n % 10 - (n // 10) % 10) + digit_distance(n // 10) - -# Alternate solution 1 -def digit_distance_alt(n): - def helper(prev, n): - if n == 0: - return 0 - dist = abs(prev - n % 10) - return dist + helper(n % 10, n // 10) - return helper(n % 10, n // 10) - -# Alternate solution 2 -def digit_distance_alt_2(n): - def helper(dist, prev, n): - if n == 0: - return dist - dist += abs(prev - n % 10) - prev = n % 10 - n //= 10 - return helper(dist, prev, n) - return helper(0, n % 10, n // 10)
- -
- -

Use Ok to test your code:

python3 ok -q digit_distance
- -
- -
-The equivalent iterative version of the problem might look something like this: - -
def distance(n):
-    dist = 0
-    prev = n % 10
-    n //= 10
-    while n > 0:
-        dist += abs(prev - n % 10)
-        prev = n % 10
-        n //= 10
-    return dist
- - -

The main idea is that we take each parameter in our iterative solution and define those -parameters as inputs into our helper function. The recursive solution otherwise follows -the same body as above, where we take the last two digits of the number, compute the -distance between them, then shift the number down by one digit by calculating n // 10.

- -

This skeleton can be simplified when implemented with recursion (see alternate solution), -but reusing the above iterative body is sufficient for this problem. -

- - - - -

Q3: Interleaved Sum

- - -

Write a function interleaved_sum, which takes in a number n and -two one-argument functions: odd_func and even_func. It applies odd_func -to every odd number and even_func to every even number from 1 to n inclusive -and returns the sum.

- -

For example, executing interleaved_sum(5, lambda x: x, lambda x: x * x) -returns 1 + 2*2 + 3 + 4*4 + 5 = 29.

- -

Important: Implement this function without using any loops or directly testing if a number -is odd or even -- aka modulos (%) are not allowed! Instead of directly checking whether a number -is even or odd, start with 1, which you know is an odd number.

- -

Hint: Introduce an inner helper function that takes an odd number k and -computes an interleaved sum from k to n (including n).

- - - -
def interleaved_sum(n, odd_func, even_func):
-    """Compute the sum odd_func(1) + even_func(2) + odd_func(3) + ..., up
-    to n.
-
-    >>> identity = lambda x: x
-    >>> square = lambda x: x * x
-    >>> triple = lambda x: x * 3
-    >>> interleaved_sum(5, identity, square) # 1   + 2*2 + 3   + 4*4 + 5
-    29
-    >>> interleaved_sum(5, square, identity) # 1*1 + 2   + 3*3 + 4   + 5*5
-    41
-    >>> interleaved_sum(4, triple, square)   # 1*3 + 2*2 + 3*3 + 4*4
-    32
-    >>> interleaved_sum(4, square, triple)   # 1*1 + 2*3 + 3*3 + 4*3
-    28
-    >>> from construct_check import check
-    >>> check(HW_SOURCE_FILE, 'interleaved_sum', ['While', 'For', 'Mod']) # ban loops and %
-    True
-    >>> check(HW_SOURCE_FILE, 'interleaved_sum', ['BitAnd', 'BitOr', 'BitXor']) # ban bitwise operators, don't worry about these if you don't know what they are
-    True
-    """
-
def sum_from(k): - if k > n: - return 0 - elif k == n: - return odd_func(k) - else: - return odd_func(k) + even_func(k+1) + sum_from(k + 2) - return sum_from(1)
- -
- -

Use Ok to test your code:

python3 ok -q interleaved_sum
- -
- - -

Q4: Count Coins

- - -

Given a positive integer total, a set of coins makes change for total if -the sum of the values of the coins is total. -Here we will use standard US Coin values: 1, 5, 10, 25. -For example, the following sets make change for 15:

- -
    -
  • 15 1-cent coins
  • -
  • 10 1-cent, 1 5-cent coins
  • -
  • 5 1-cent, 2 5-cent coins
  • -
  • 5 1-cent, 1 10-cent coins
  • -
  • 3 5-cent coins
  • -
  • 1 5-cent, 1 10-cent coin
  • -
- -

Thus, there are 6 ways to make change for 15. Write a recursive function -count_coins that takes a positive integer total and returns the number of -ways to make change for total using coins.

- -

You can use either of the functions given to you:

- -
    -
  • next_larger_coin will return the next larger coin denomination from the - input, i.e. next_larger_coin(5) is 10.
  • -
  • next_smaller_coin will return the next smaller coin denomination from the - input, i.e. next_smaller_coin(5) is 1.
  • -
  • Either function will return None if the next coin value does not exist
  • -
- -

There are two main ways in which you can approach this problem. -One way uses next_larger_coin, and another uses next_smaller_coin. -It is up to you which one you want to use!

- -

Important: Use recursion; the tests will fail if you use loops.

- -

Hint: -Refer to the implementation of count_partitions for an example -of how to count the ways to sum up to a final value with smaller parts. -If you need to keep track of more than one value across recursive calls, -consider writing a helper function.

- - - -
def next_larger_coin(coin):
-    """Returns the next larger coin in order.
-    >>> next_larger_coin(1)
-    5
-    >>> next_larger_coin(5)
-    10
-    >>> next_larger_coin(10)
-    25
-    >>> next_larger_coin(2) # Other values return None
-    """
-    if coin == 1:
-        return 5
-    elif coin == 5:
-        return 10
-    elif coin == 10:
-        return 25
-
-def next_smaller_coin(coin):
-    """Returns the next smaller coin in order.
-    >>> next_smaller_coin(25)
-    10
-    >>> next_smaller_coin(10)
-    5
-    >>> next_smaller_coin(5)
-    1
-    >>> next_smaller_coin(2) # Other values return None
-    """
-    if coin == 25:
-        return 10
-    elif coin == 10:
-        return 5
-    elif coin == 5:
-        return 1
-
-def count_coins(total):
-    """Return the number of ways to make change using coins of value of 1, 5, 10, 25.
-    >>> count_coins(15)
-    6
-    >>> count_coins(10)
-    4
-    >>> count_coins(20)
-    9
-    >>> count_coins(100) # How many ways to make change for a dollar?
-    242
-    >>> count_coins(200)
-    1463
-    >>> from construct_check import check
-    >>> # ban iteration
-    >>> check(HW_SOURCE_FILE, 'count_coins', ['While', 'For'])
-    True
-    """
-
def constrained_count(total, smallest_coin): - if total == 0: - return 1 - if total < 0: - return 0 - if smallest_coin == None: - return 0 - without_coin = constrained_count(total, next_larger_coin(smallest_coin)) - with_coin = constrained_count(total - smallest_coin, smallest_coin) - return without_coin + with_coin - return constrained_count(total, 1) - - # Alternate solution: using next_smaller_coin - def constrained_count_small(total, largest_coin): - if total == 0: - return 1 - if total < 0: - return 0 - if largest_coin == None: - return 0 - without_coin = constrained_count_small(total, next_smaller_coin(largest_coin)) - with_coin = constrained_count_small(total - largest_coin, largest_coin) - return without_coin + with_coin - return constrained_count_small(total, 25)
- -
- -

Use Ok to test your code:

python3 ok -q count_coins
- -
- -
- -

This is remarkably similar to the count_partitions problem, with a -few minor differences:

- -
    -
  • A maximum partition size is not given, so we need to create a helper - function that takes in two arguments and also create another helper function - to find the max coin.
  • -
  • Partition size is not linear. To get the next partition you need to call - next_larger_coin if you are counting up (i.e. from the smallest coin to the - largest coin), or next_smaller_coin if you are counting down.
  • -
- -
- - -

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!

- -
    -
  1. Fall 2017 MT1 Q4a: Digital
  2. -
  3. Summer 2018 MT1 Q5a: Won't You Be My Neighbor?
  4. -
  5. Fall 2019 Final Q6b: Palindromes
  6. -
- - -

Just For Fun Questions

- - -

The questions below are out of scope for 61A. You can try them if you want an extra challenge, but they're just puzzles that are not required for the course. Almost all students will skip them, and that's fine. We will not be prioritizing support for these questions on Ed or during Office Hours.

- - -

Q5: Towers of Hanoi

- - -

- -A classic puzzle called the Towers of Hanoi is a game that consists of three -rods, and a number of disks of different sizes which can slide onto any rod. -The puzzle starts with n disks in a neat stack in ascending order of size on -a start rod, the smallest at the top, forming a conical shape. - -Towers of Hanoi - -The objective of the puzzle is to move the entire stack to an end rod, -obeying the following rules: - -
    -
  • Only one disk may be moved at a time.
  • -
  • Each move consists of taking the top (smallest) disk from one of the rods and - sliding it onto another rod, on top of the other disks that may already be - present on that rod.
  • -
  • No disk may be placed on top of a smaller disk.
  • -
- -Complete the definition of move_stack, which prints out the steps required to -move n disks from the start rod to the end rod without violating the -rules. The provided print_move function will print out the step to move a -single disk from the given origin to the given destination. - -

Hint: -Draw out a few games with various n on a piece of paper and try to -find a pattern of disk movements that applies to any n. In your solution, -take the recursive leap of faith whenever you need to move any amount of -disks less than n from one rod to another. If you need more help, see -the following hints.

- - - -
- -

See the following animation of the Towers of Hanoi, found on -Wikimedia -by user Trixx.

- - -
-
- - -
- -

The strategy used in Towers of Hanoi is to move all but the bottom disc to the -second peg, then moving the bottom disc to the third peg, then moving all but -the second disc from the second to the third peg.

- -
-
- - -
- -

One thing you don't need to worry about is collecting all the steps. -print effectively "collects" all the results in the terminal as long as you -make sure that the moves are printed in order.

- -
- - - -
def print_move(origin, destination):
-    """Print instructions to move a disk."""
-    print("Move the top disk from rod", origin, "to rod", destination)
-
-def move_stack(n, start, end):
-    """Print the moves required to move n disks on the start pole to the end
-    pole without violating the rules of Towers of Hanoi.
-
-    n -- number of disks
-    start -- a pole position, either 1, 2, or 3
-    end -- a pole position, either 1, 2, or 3
-
-    There are exactly three poles, and start and end must be different. Assume
-    that the start pole has at least n disks of increasing size, and the end
-    pole is either empty or has a top disk larger than the top n start disks.
-
-    >>> move_stack(1, 1, 3)
-    Move the top disk from rod 1 to rod 3
-    >>> move_stack(2, 1, 3)
-    Move the top disk from rod 1 to rod 2
-    Move the top disk from rod 1 to rod 3
-    Move the top disk from rod 2 to rod 3
-    >>> move_stack(3, 1, 3)
-    Move the top disk from rod 1 to rod 3
-    Move the top disk from rod 1 to rod 2
-    Move the top disk from rod 3 to rod 2
-    Move the top disk from rod 1 to rod 3
-    Move the top disk from rod 2 to rod 1
-    Move the top disk from rod 2 to rod 3
-    Move the top disk from rod 1 to rod 3
-    """
-    assert 1 <= start <= 3 and 1 <= end <= 3 and start != end, "Bad start/end"
-
if n == 1: - print_move(start, end) - else: - other = 6 - start - end - move_stack(n-1, start, other) - print_move(start, end) - move_stack(n-1, other, end)
- -
- -

Use Ok to test your code:

python3 ok -q move_stack
- -
- -
- -

To solve the Towers of Hanoi problem for n disks, we need to do three -steps:

- -
    -
  1. Move everything but the last disk (n-1 disks) to someplace in the - middle (not the start nor the end rod).
  2. -
  3. Move the last disk (a single disk) to the end rod. This must occur - after step 1 (we have to move everything above it away first)!
  4. -
  5. Move everything but the last disk (the disks from step 1) from the - middle on top of the end rod.
  6. -
- -

We take advantage of the fact that the recursive function move_stack is -guaranteed to move n disks from start to end while obeying the rules -of Towers of Hanoi. The only thing that remains is to make sure that we -have set up the playing board to make that possible.

- -

Since we move a disk to end rod, we run the risk of move_stack doing an -improper move (big disk on top of small disk). But since we're moving the -biggest disk possible, nothing in the n-1 disks above that is bigger. -Therefore, even though we do not explicitly state the Towers of Hanoi -constraints, we can still carry out the correct steps.

- -

Video walkthrough:

YouTube link

- -
- - -

Q6: Anonymous Factorial

- - -

This question demonstrates that it's possible to write recursive functions -without assigning them a name in the global frame.

- -

The recursive factorial function can be written as a single expression -by using a -conditional expression.

- -
>>> fact = lambda n: 1 if n == 1 else mul(n, fact(sub(n, 1)))
->>> fact(5)
-120
- -

However, this implementation relies on the fact (no pun intended) that -fact has a name, to which we refer in the body of fact. To write a -recursive function, we have always given it a name using a def or -assignment statement so that we can refer to the function within its -own body. In this question, your job is to define fact recursively -without giving it a name!

- -

Write an expression that computes n factorial using only call -expressions, conditional expressions, and lambda expressions (no -assignment or def statements).

- -

Note: -You are not allowed to use make_anonymous_factorial in your return -expression.

- -

The sub and mul functions from the operator module are the only -built-in functions required to solve this problem.

- - - -
from operator import sub, mul
-
-def make_anonymous_factorial():
-    """Return the value of an expression that computes factorial.
-
-    >>> make_anonymous_factorial()(5)
-    120
-    >>> from construct_check import check
-    >>> # ban any assignments or recursion
-    >>> check(HW_SOURCE_FILE, 'make_anonymous_factorial',
-    ...     ['Assign', 'AnnAssign', 'AugAssign', 'NamedExpr', 'FunctionDef', 'Recursion'])
-    True
-    """
-
return (lambda f: lambda k: f(f, k))(lambda f, k: k if k == 1 else mul(k, f(f, sub(k, 1)))) - # Alternate solution: - return (lambda f: f(f))(lambda f: lambda x: 1 if x == 0 else x * f(f)(x - 1))
- -
- -

Use Ok to test your code:

python3 ok -q make_anonymous_factorial
- -
- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/hw/sol-hw03/ok b/hw/sol-hw03/ok deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/hw/sol-hw03/ok and /dev/null differ diff --git a/hw/sol-hw04/assets/mobile-planet-labeled.png b/hw/sol-hw04/assets/mobile-planet-labeled.png deleted file mode 100644 index 0720a59378..0000000000 Binary files a/hw/sol-hw04/assets/mobile-planet-labeled.png and /dev/null differ diff --git a/hw/sol-hw04/assets/mobile-planet.png b/hw/sol-hw04/assets/mobile-planet.png deleted file mode 100644 index e49f4e6364..0000000000 Binary files a/hw/sol-hw04/assets/mobile-planet.png and /dev/null differ diff --git a/hw/sol-hw04/construct_check.py b/hw/sol-hw04/construct_check.py deleted file mode 100644 index bee096e7e4..0000000000 --- a/hw/sol-hw04/construct_check.py +++ /dev/null @@ -1,180 +0,0 @@ -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 deleted file mode 100644 index 0c6a617d18..0000000000 --- a/hw/sol-hw04/hw04.ok +++ /dev/null @@ -1,26 +0,0 @@ -{ - "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 deleted file mode 100644 index 9898785364..0000000000 --- a/hw/sol-hw04/hw04.py +++ /dev/null @@ -1,234 +0,0 @@ -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 deleted file mode 100644 index 62719667ef..0000000000 Binary files a/hw/sol-hw04/hw04.zip and /dev/null differ diff --git a/hw/sol-hw04/index.html b/hw/sol-hw04/index.html deleted file mode 100644 index ce3230f0a6..0000000000 --- a/hw/sol-hw04/index.html +++ /dev/null @@ -1,730 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Homework 4 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Homework 4 Solutions - - - - - - -

-
- -

Solution Files

-

You can find the solutions in hw04.py.

- - -

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.

- - -

YouTube link

-
- -
- - -

Sequences

- - - -

Q1: Deep Map

- - -

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.

- -

Mobile example

- -

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.

- -

Labeled Mobile example

- -

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:

- -
    -
  1. 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.
  2. -
  3. Each of the mobiles hanging at the end of its arms is itself balanced.
  4. -
- -

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.

- -
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)
- -

Use Ok to test your code:

python3 ok -q balanced
- -
- -
- -

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:

    - -
      -
    1. 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.
    2. -
    3. 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.
    4. -
  • -
  • 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!

- -
    -
  1. Summer 2021 MT Q4: Maximum Exponen-tree-ation
  2. -
  3. Summer 2019 MT Q8: Leaf It To Me
  4. -
  5. Summer 2017 MT Q9: Temmie Flakes
  6. -
- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/hw/sol-hw04/ok b/hw/sol-hw04/ok deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/hw/sol-hw04/ok and /dev/null differ diff --git a/hw/sol-hw05/construct_check.py b/hw/sol-hw05/construct_check.py deleted file mode 100644 index bee096e7e4..0000000000 --- a/hw/sol-hw05/construct_check.py +++ /dev/null @@ -1,180 +0,0 @@ -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-hw05/hw05.ok b/hw/sol-hw05/hw05.ok deleted file mode 100644 index 7563437b1c..0000000000 --- a/hw/sol-hw05/hw05.ok +++ /dev/null @@ -1,25 +0,0 @@ -{ - "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/sol-hw05/hw05.py b/hw/sol-hw05/hw05.py deleted file mode 100644 index 2c8e95063d..0000000000 --- a/hw/sol-hw05/hw05.py +++ /dev/null @@ -1,151 +0,0 @@ -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) - - -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] - """ - first_a, first_b = next(a), next(b) - while True: - if first_a == first_b: - yield first_a - first_a, first_b = next(a), next(b) - elif first_a < first_b: - yield first_a - first_a = next(a) - else: - yield first_b - first_b = next(b) - - -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 - - - -# 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-hw05/hw05.zip b/hw/sol-hw05/hw05.zip deleted file mode 100644 index 3b1bdc49a4..0000000000 Binary files a/hw/sol-hw05/hw05.zip and /dev/null differ diff --git a/hw/sol-hw05/index.html b/hw/sol-hw05/index.html deleted file mode 100644 index fdd7329960..0000000000 --- a/hw/sol-hw05/index.html +++ /dev/null @@ -1,520 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Homework 5 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Homework 5 Solutions - - - - - - -

-
- -

Solution Files

-

You can find the solutions in hw05.py.

- - -

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.

- - -

YouTube link

-
- - -

Q1: Infinite Hailstone

- - -

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:

- -
    -
  1. Pick a positive integer n as the start.
  2. -
  3. If n is even, divide it by 2.
  4. -
  5. If n is odd, multiply it by 3 and add 1.
  6. -
  7. Continue this process until n is 1.
  8. -
- -

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.

- - - -
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]
-    """
-
first_a, first_b = next(a), next(b) - while True: - if first_a == first_b: - yield first_a - first_a, first_b = next(a), next(b) - elif first_a < first_b: - yield first_a - first_a = next(a) - else: - yield first_b - first_b = next(b)
- -
- -

Use Ok to test your code:

python3 ok -q merge
- -
- - -

Q3: Yield Paths

- - -

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!

- -
    -
  1. Summer 2018 Final Q7a,b: Streams and Jennyrators
  2. -
  3. Spring 2019 Final Q1: Iterators are inevitable
  4. -
  5. Spring 2021 MT2 Q8: The Tree of L-I-F-E
  6. -
  7. Summer 2016 Final Q8: Zhen-erators Produce Power
  8. -
  9. Spring 2018 Final Q4a: Apply Yourself
  10. -
- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/hw/sol-hw05/ok b/hw/sol-hw05/ok deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/hw/sol-hw05/ok and /dev/null differ diff --git a/hw/sol-hw06/construct_check.py b/hw/sol-hw06/construct_check.py deleted file mode 100644 index bee096e7e4..0000000000 --- a/hw/sol-hw06/construct_check.py +++ /dev/null @@ -1,180 +0,0 @@ -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-hw06/hw06.ok b/hw/sol-hw06/hw06.ok deleted file mode 100644 index 6c88d9378f..0000000000 --- a/hw/sol-hw06/hw06.ok +++ /dev/null @@ -1,24 +0,0 @@ -{ - "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/sol-hw06/hw06.py b/hw/sol-hw06/hw06.py deleted file mode 100644 index 4f7c33c291..0000000000 --- a/hw/sol-hw06/hw06.py +++ /dev/null @@ -1,212 +0,0 @@ -passphrase = 'taco' - -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.' - """ - 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 + '.' - - -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 - - -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> - """ - if lnk is Link.empty: - return - elif isinstance(lnk.first, Link): - deep_map_mut(func, lnk.first) - else: - lnk.first = func(lnk.first) - deep_map_mut(func, lnk.rest) - - -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 - - -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/sol-hw06/hw06.scm b/hw/sol-hw06/hw06.scm deleted file mode 100644 index 9eed62c76f..0000000000 --- a/hw/sol-hw06/hw06.scm +++ /dev/null @@ -1,63 +0,0 @@ -(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)))))) - -(define (repeatedly-cube n x) - (if (zero? n) - x - (let ((y (repeatedly-cube (- n 1) x))) - (* y y y)))) - -(define (cddr s) (cdr (cdr s))) - -(define (cadr s) (car (cdr s))) - -(define (caddr s) (car (cddr s))) - -(define (ascending? s) - (if (or (null? s) (null? (cdr s))) - #t - (and (<= (car s) (car (cdr s))) - (ascending? (cdr s))))) - -(define (my-filter pred s) - (cond - ((null? s) - '()) - ((pred (car s)) - (cons (car s) (my-filter pred (cdr s)))) - (else - (my-filter pred (cdr s))))) - -(define (no-repeats s) - (if (null? s) - s - (cons (car s) - (no-repeats - (filter (lambda (x) (not (= (car s) x))) (cdr s)))))) - -; helper function -; returns the values of lst that are bigger than x -; e.g., (larger-values 3 '(1 2 3 4 5 1 2 3 4 5)) --> (4 5 4 5) -(define (larger-values x lst) - (filter (lambda (v) (> v x)) lst)) - -(define (longest-increasing-subsequence lst) - (if (null? lst) - nil - (begin (define first (car lst)) - (define rest (cdr lst)) - (define large-values-rest - (larger-values first rest)) - (define with-first - (cons (car lst) - (longest-increasing-subsequence large-values-rest))) - (define without-first - (longest-increasing-subsequence rest)) - (if (> (length with-first) (length without-first)) - with-first - without-first)))) diff --git a/hw/sol-hw06/hw06.zip b/hw/sol-hw06/hw06.zip deleted file mode 100644 index d724484ba2..0000000000 Binary files a/hw/sol-hw06/hw06.zip and /dev/null differ diff --git a/hw/sol-hw06/index.html b/hw/sol-hw06/index.html deleted file mode 100644 index 5f29ece661..0000000000 --- a/hw/sol-hw06/index.html +++ /dev/null @@ -1,704 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Homework 6 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Homework 6 Solutions - - - - - - -

-
- -

Solution Files

-

You can find the solutions in hw06.py.

- - -

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.

- - -

YouTube link

-
- - -

Midsemester Survey

- - - -

Q1: Mid-Semester Feedback

- - -

As part of this assignment, fill out the -Mid-Semester Feedback form.

- -

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:

- -
>>> feeling = 'love'
->>> course = '61A!'
->>> f'I {feeling} {course}'
-'I love 61A!'
- -

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.

    - -
      -
    1. No stock
    2. -
    3. Not enough balance
    4. -
    5. Leftover balance after purchase (return change to customer)
    6. -
    7. No leftover balance after purchase
    8. -
  • -
  • 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:

    - -
    if difference != 0:
    -    return ...
    -else:
    -    return ...
    - -

    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.

- -
>>> s = Link(1, Link(2, Link(3, Link(4))))
->>> isinstance(s, Link)
-True
->>> isinstance(s, int)
-False
- - - -

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>
-    """
-
if lnk is Link.empty: - return - elif isinstance(lnk.first, Link): - deep_map_mut(func, lnk.first) - else: - lnk.first = func(lnk.first) - deep_map_mut(func, lnk.rest)
- -
- -

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)))))
-    """
-
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!

- -

Object-Oriented Programming

- -
    -
  1. Spring 2022 MT2 Q8: CS61A Presents The Game of Hoop.
  2. -
  3. Fall 2020 MT2 Q3: Sparse Lists
  4. -
  5. Fall 2019 MT2 Q7: Version 2.0
  6. -
- -

Linked Lists

- -
    -
  1. Fall 2020 Final Q3: College Party
  2. -
  3. Fall 2018 MT2 Q6: Dr. Frankenlink
  4. -
  5. Spring 2017 MT1 Q5: Insert
  6. -
- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/hw/sol-hw06/ok b/hw/sol-hw06/ok deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/hw/sol-hw06/ok and /dev/null differ diff --git a/hw/sol-hw06/scheme b/hw/sol-hw06/scheme deleted file mode 100644 index 346c9b5fa7..0000000000 Binary files a/hw/sol-hw06/scheme and /dev/null differ diff --git a/hw/sol-hw06/tests/ascending.py b/hw/sol-hw06/tests/ascending.py deleted file mode 100644 index 559bdf7b13..0000000000 --- a/hw/sol-hw06/tests/ascending.py +++ /dev/null @@ -1,44 +0,0 @@ -test = { - 'name': 'ascending?', - 'points': 1, - 'suites': [ - { - 'type': 'scheme', - 'setup': r""" - scm> (load-all ".") - """, - 'cases': [ - { - 'code': r""" - scm> (ascending? '(1 2 3 4 5)) ; #t or #f - #t - """, - }, - { - 'code': r""" - scm> (ascending? '(1 5 2 4 3)) ; #t or #f - #f - """, - }, - { - 'code': r""" - scm> (ascending? '(2 2)) ; #t or #f - #t - """, - }, - { - 'code': r""" - scm> (ascending? '(1 2 2 4 3)) ; #t or #f - #f - """, - }, - { - 'code': r""" - scm> (ascending? '()) ; #t or #f - #t - """, - }, - ], - }, - ] -} \ No newline at end of file diff --git a/hw/sol-hw06/tests/cadr-caddr.py b/hw/sol-hw06/tests/cadr-caddr.py deleted file mode 100644 index 5e9e817e46..0000000000 --- a/hw/sol-hw06/tests/cadr-caddr.py +++ /dev/null @@ -1,38 +0,0 @@ -test = { - 'name': 'cadr-caddr', - 'points': 1, - 'suites': [ - { - 'type': 'scheme', - 'setup': r""" - scm> (load-all ".") - """, - 'cases': [ - { - 'code': r""" - scm> (cddr '(1 2 3 4)) - (3 4) - """, - 'hidden': False, - 'locked': False - }, - { - 'code': r""" - scm> (cadr '(1 2 3 4)) - 2 - """, - 'hidden': False, - 'locked': False - }, - { - 'code': r""" - scm> (caddr '(1 2 3 4)) - 3 - """, - 'hidden': False, - 'locked': False - }, - ], - }, - ] -} \ No newline at end of file diff --git a/hw/sol-hw06/tests/filter.py b/hw/sol-hw06/tests/filter.py deleted file mode 100644 index 84f092a106..0000000000 --- a/hw/sol-hw06/tests/filter.py +++ /dev/null @@ -1,67 +0,0 @@ -test = { - 'name': 'my-filter', - 'points': 1, - 'suites': [ - { - 'scored': True, - 'setup': """ - scm> (load-all ".") - """, - 'type': 'scheme', - 'cases': [ - { - 'code': """ - scm> (my-filter even? '(1 2 3 4)) - (2 4) - """, - 'hidden': False - }, - { - 'code': """ - scm> (my-filter odd? '(1 3 5)) - (1 3 5) - """, - 'hidden': False - }, - { - 'code': """ - scm> (my-filter odd? '(2 4 6 1)) - (1) - """, - 'hidden': False - }, - { - 'code': """ - scm> (my-filter even? '(3)) - () - """, - 'hidden': False - }, - { - 'code': """ - scm> (my-filter odd? nil) - () - """, - 'hidden': False - }, - ] - }, - { - 'scored': True, - 'setup': """ - scm> (define filter nil) - scm> (load-all ".") - """, - 'type': 'scheme', - 'cases': [ - { - 'code': """ - scm> (my-filter even? '(1 2 3 4)) ; checks you dont use builtin filter - (2 4) - """, - 'hidden': False - } - ] - } - ] -} \ No newline at end of file diff --git a/hw/sol-hw06/tests/longest-increasing-subsequence.py b/hw/sol-hw06/tests/longest-increasing-subsequence.py deleted file mode 100644 index 6b626a355f..0000000000 --- a/hw/sol-hw06/tests/longest-increasing-subsequence.py +++ /dev/null @@ -1,33 +0,0 @@ -test = { - 'name': 'longest-increasing-subsequence', - 'points': 1, - 'suites': [ - { - 'type': 'scheme', - 'cases': [ - { - 'code': r""" - scm> (longest-increasing-subsequence '()) - () - scm> (longest-increasing-subsequence '(1)) - (1) - scm> (longest-increasing-subsequence '(1 2 3)) - (1 2 3) - scm> (longest-increasing-subsequence '(1 9 2 3)) - (1 2 3) - scm> (longest-increasing-subsequence '(1 9 8 7 6 5 4 3 2 3)) - (1 2 3) - scm> (longest-increasing-subsequence '(1 9 8 7 2 3 6 5 4 5)) - (1 2 3 4 5) - scm> (longest-increasing-subsequence '(1 2 3 4 9 3 4 1 10 5)) - (1 2 3 4 9 10) - """, - 'locked' : False, - }, - ], - 'setup': r""" - scm> (load-all ".") - """, - }, - ] -} \ No newline at end of file diff --git a/hw/sol-hw06/tests/no_repeats.py b/hw/sol-hw06/tests/no_repeats.py deleted file mode 100644 index 477c7bc58b..0000000000 --- a/hw/sol-hw06/tests/no_repeats.py +++ /dev/null @@ -1,84 +0,0 @@ -test = { - 'name': 'no-repeats', - 'points': 1, - 'suites': [ - { - 'scored': True, - 'setup': """ - scm> (load-all ".") - """, - 'type': 'scheme', - 'cases': [ - { - 'code': """ - scm> (no-repeats '(5 4 3 2 1)) - (5 4 3 2 1) - """, - 'hidden': False, - "locked": False - }, - { - 'code': """ - scm> (no-repeats '(5 4 3 2 1 1)) - (5 4 3 2 1) - """, - 'hidden': False, - "locked": False - }, - { - 'code': """ - scm> (no-repeats '(5 5 4 3 2 1)) - (5 4 3 2 1) - """, - 'hidden': False, - "locked": False - }, - { - 'code': """ - scm> (no-repeats '(12)) - (12) - """, - 'hidden': False, - "locked": False - }, - { - 'code': """ - scm> (no-repeats '(1 1 1 1 1 1)) - (1) - """, - 'hidden': False, - "locked": False - }, - ] - }, - { - 'scored': True, - 'setup': """ - scm> (load-all ".") - """, - 'type': 'scheme', - 'cases': [ - { - 'code': """ - scm> (no-repeats (list 5 4 2)) - (5 4 2) - """, - 'hidden': False, - "locked": False - }, - { - 'code': """ - scm> (no-repeats (list 5 4 5 4 2 2)) - (5 4 2) - scm> (no-repeats (list 5 5 5 5 5)) - (5) - scm> (no-repeats ()) - () - """, - 'hidden': False, - "locked": False - }, - ] - } - ] -} \ No newline at end of file diff --git a/hw/sol-hw06/tests/pow.py b/hw/sol-hw06/tests/pow.py deleted file mode 100644 index 67d7398ed6..0000000000 --- a/hw/sol-hw06/tests/pow.py +++ /dev/null @@ -1,47 +0,0 @@ -test = { - 'name': 'pow', - 'points': 1, - 'suites': [ - { - 'scored': True, - 'setup': """ - scm> (load-all ".") - """, - 'type': 'scheme', - 'cases': [ - { - 'code': """ - scm> (pow 2 5) - 32 - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> (pow 10 3) - 1000 - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> (pow 3 3) - 27 - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> (pow 1 100000000000000) ; make sure this doesn't run forever! - 1 - """, - 'hidden': False, - 'locked': False - }, - ], - }, - ] -} \ No newline at end of file diff --git a/hw/sol-hw06/tests/repeatedly-cube.py b/hw/sol-hw06/tests/repeatedly-cube.py deleted file mode 100644 index cad91f867a..0000000000 --- a/hw/sol-hw06/tests/repeatedly-cube.py +++ /dev/null @@ -1,27 +0,0 @@ -test = { - 'name': 'repeatedly-cube', - 'points': 1, - 'suites': [ - { - 'type': 'scheme', - 'scored': True, - 'setup': """ - scm> (load-all ".") - """, - 'cases': [ - { - 'code': """ - scm> (repeatedly-cube 3 1) - 1 - scm> (repeatedly-cube 2 2) - 512 - scm> (repeatedly-cube 3 2) - 134217728 - """, - 'hidden': False, - 'locked' : False - }, - ] - } - ] -} \ No newline at end of file diff --git a/hw/sol-hw07/assets/after.png b/hw/sol-hw07/assets/after.png deleted file mode 100644 index cf8e95e510..0000000000 Binary files a/hw/sol-hw07/assets/after.png and /dev/null differ diff --git a/hw/sol-hw07/assets/before.png b/hw/sol-hw07/assets/before.png deleted file mode 100644 index 6da4249e85..0000000000 Binary files a/hw/sol-hw07/assets/before.png and /dev/null differ diff --git a/hw/sol-hw07/hw07.ok b/hw/sol-hw07/hw07.ok deleted file mode 100644 index 823c93915b..0000000000 --- a/hw/sol-hw07/hw07.ok +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "Homework 7", - "endpoint": "cal/cs61a/fa24/hw07", - "src": [ - "hw07.scm" - ], - "tests": { - "hw*.py": "doctest", - "tests/*.py": "ok_test" - }, - "default_tests": [ - "pow", - "repeatedly-cube", - "cadr-caddr" - ], - "protocols": [ - "restore", - "file_contents", - "unlock", - "grading", - "analytics", - "help", - "backup" - ] -} \ No newline at end of file diff --git a/hw/sol-hw07/hw07.scm b/hw/sol-hw07/hw07.scm deleted file mode 100644 index 37b948760f..0000000000 --- a/hw/sol-hw07/hw07.scm +++ /dev/null @@ -1,19 +0,0 @@ -(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)))))) - -(define (repeatedly-cube n x) - (if (zero? n) - x - (let ((y (repeatedly-cube (- n 1) x))) - (* y y y)))) - -(define (cddr s) (cdr (cdr s))) - -(define (cadr s) (car (cdr s))) - -(define (caddr s) (car (cddr s))) diff --git a/hw/sol-hw07/hw07.sql b/hw/sol-hw07/hw07.sql deleted file mode 100644 index c553724d65..0000000000 --- a/hw/sol-hw07/hw07.sql +++ /dev/null @@ -1,54 +0,0 @@ -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; - - --- 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; - - --- The size of each dog -CREATE TABLE size_of_dogs AS - SELECT name, size FROM dogs, sizes - WHERE height > min AND height <= max; - - --- 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; - - --- 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); - diff --git a/hw/sol-hw07/hw07.zip b/hw/sol-hw07/hw07.zip deleted file mode 100644 index 7b3f401164..0000000000 Binary files a/hw/sol-hw07/hw07.zip and /dev/null differ diff --git a/hw/sol-hw07/index.html b/hw/sol-hw07/index.html deleted file mode 100644 index 20980e3dbd..0000000000 --- a/hw/sol-hw07/index.html +++ /dev/null @@ -1,476 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Homework 7 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Homework 7 Solutions - - - - - - -

-
- -

Solution Files

-

You can find the solutions in hw07.scm.

- - -

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.

- - - - -

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.

- - -

YouTube link

-
- - -

Q1: Pow

- - -

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:

- -
    -
  1. x2y = (xy)2
  2. -
  3. x2y+1 = x(xy)2
  4. -
- -

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
- - - -

For information on let, see the Scheme spec.

- - - -
(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 x n - 1 times. Then the cube of y is the result of cubing x n 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.

- - - -
(define (cddr s)
-  (cdr (cdr s)))
-
-(define (cadr s)
-
(car (cdr s))
) - -(define (caddr s) -
(car (cddr s))
)
- - - -
- -
- -

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.

- -
- -

Use Ok to test your code:

python3 ok -q cadr-caddr
- -
- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/hw/sol-hw07/ok b/hw/sol-hw07/ok deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/hw/sol-hw07/ok and /dev/null differ diff --git a/hw/sol-hw07/scheme b/hw/sol-hw07/scheme deleted file mode 100644 index 2174145cff..0000000000 Binary files a/hw/sol-hw07/scheme and /dev/null differ diff --git a/hw/sol-hw07/sqlite_shell.py b/hw/sol-hw07/sqlite_shell.py deleted file mode 100644 index cf1d3686fc..0000000000 --- a/hw/sol-hw07/sqlite_shell.py +++ /dev/null @@ -1,681 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the MIT license - -# A simple SQLite shell that uses the built-in Python adapter. - -import codecs -import io -import os -import sys -import sqlite3 -import time -import warnings - -try: FileNotFoundError -except NameError: FileNotFoundError = OSError - -if str != bytes: buffer = bytes -if str != bytes: unicode = str - -try: import msvcrt -except ImportError: msvcrt = None - -CP_UTF8 = 65001 -pythonapi = None -if msvcrt: - import ctypes - (BOOL, DWORD, HANDLE, UINT) = (ctypes.c_long, ctypes.c_ulong, ctypes.c_void_p, ctypes.c_uint) - GetConsoleCP = ctypes.WINFUNCTYPE(UINT)(('GetConsoleCP', ctypes.windll.kernel32)) - SetConsoleCP = ctypes.WINFUNCTYPE(BOOL, UINT)(('SetConsoleCP', ctypes.windll.kernel32)) - GetConsoleOutputCP = ctypes.WINFUNCTYPE(UINT)(('GetConsoleOutputCP', ctypes.windll.kernel32)) - SetConsoleOutputCP = ctypes.WINFUNCTYPE(BOOL, UINT)(('SetConsoleOutputCP', ctypes.windll.kernel32)) - GetConsoleMode = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.POINTER(DWORD), use_last_error=True)(('GetConsoleMode', ctypes.windll.kernel32)) - GetNumberOfConsoleInputEvents = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.POINTER(DWORD), use_last_error=True)(('GetNumberOfConsoleInputEvents', ctypes.windll.kernel32)) - ReadConsoleW = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.c_void_p, DWORD, ctypes.POINTER(DWORD), ctypes.c_void_p, use_last_error=True)(('ReadConsoleW', ctypes.windll.kernel32)) - WriteConsoleW = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.c_void_p, DWORD, ctypes.POINTER(DWORD), ctypes.c_void_p, use_last_error=True)(('WriteConsoleW', ctypes.windll.kernel32)) - class Py_buffer(ctypes.Structure): _fields_ = [('buf', ctypes.c_void_p), ('obj', ctypes.py_object), ('len', ctypes.c_ssize_t), ('itemsize', ctypes.c_ssize_t), ('readonly', ctypes.c_int), ('ndim', ctypes.c_int), ('format', ctypes.c_char_p), ('shape', ctypes.POINTER(ctypes.c_ssize_t)), ('strides', ctypes.POINTER(ctypes.c_ssize_t)), ('suboffsets', ctypes.POINTER(ctypes.c_ssize_t))] + ([('smalltable', ctypes.c_ssize_t * 2)] if sys.version_info[0] <= 2 else []) + [('internal', ctypes.c_void_p)] - try: from ctypes import pythonapi - except ImportError: pass -if pythonapi: - def getbuffer(b, writable): - arr = Py_buffer() - pythonapi.PyObject_GetBuffer(ctypes.py_object(b), ctypes.byref(arr), ctypes.c_int(writable)) - try: buf = (ctypes.c_ubyte * arr.len).from_address(arr.buf) - finally: pythonapi.PyBuffer_Release(ctypes.byref(arr)) - return buf - -ENCODING = 'utf-8' - -if sys.version_info[0] < 3: - class NotASurrogateError(Exception): pass - def surrogateescape_handler(exc): - # Source: https://github.com/PythonCharmers/python-future/blob/aef57391c0cd58bf840dff5e2bc2c8c0f5b0a1b4/src/future/utils/surrogateescape.py - mystring = exc.object[exc.start:exc.end] - try: - if isinstance(exc, UnicodeDecodeError): - decoded = [] - for ch in mystring: - if isinstance(ch, int): - code = ch - else: - code = ord(ch) - if 0x80 <= code <= 0xFF: - decoded.append(unichr(0xDC00 + code)) - elif code <= 0x7F: - decoded.append(unichr(code)) - else: - raise NotASurrogateError() - decoded = str().join(decoded) - elif isinstance(exc, UnicodeEncodeError): - decoded = [] - for ch in mystring: - code = ord(ch) - if not 0xD800 <= code <= 0xDCFF: - raise NotASurrogateError() - if 0xDC00 <= code <= 0xDC7F: - decoded.append(unichr(code - 0xDC00)) - elif code <= 0xDCFF: - decoded.append(unichr(code - 0xDC00)) - else: - raise NotASurrogateError() - decoded = str().join(decoded) - else: - raise exc - except NotASurrogateError: - raise exc - return (decoded, exc.end) - codecs.register_error('surrogateescape', surrogateescape_handler) - -def exception_encode(ex, codec): - if str == bytes: - reduced = ex.__reduce__() - ex = reduced[0](*tuple(map(lambda arg: codec.decode(arg)[0] if isinstance(arg, bytes) else arg, reduced[1]))) - return ex - -def sql_commands(read_line): - delims = ['"', "'", ';', '--'] - counter = 0 - in_string = None - j = i = 0 - prev_line = None - line = None - concat = [] - while True: - if line is None: - while True: # process preprocessor directives - counter += 1 - not_in_the_middle_of_any_input = not in_string and i == j and all(map(lambda chunk_: len(chunk_) == 0, concat)) - line = read_line(counter - 1, not_in_the_middle_of_any_input, prev_line) - empty_string = line[:0] if line is not None else line - prev_line = line - if not line: - break - if not_in_the_middle_of_any_input and line.startswith("."): - yield line - line = None - else: - break - if not line: - break - j = i = 0 - if j < len(line): - (j, delim) = min(map(lambda pair: pair if pair[0] >= 0 else (len(line), pair[1]), map(lambda d: (line.find(d, j), d), in_string or delims if in_string != '--' else "\n"))) - if i < j: concat.append(line[i:j]); i = j - if not in_string: - if j < len(line): - j += len(delim) - if delim == ';': - i = j - concat.append(line[j : j + len(delim)]) # ensure delimeter is the same type as the string (it may not be due to implicit conversion) - # Eat up any further spaces until a newline - while j < len(line): - delim = line[j:j+1] - if not delim.isspace(): break - j += 1 - if delim == "\n": break - if i < j: concat.append(line[i:j]); i = j - yield empty_string.join(concat) - del concat[:] - else: - in_string = delim - else: - if j < len(line): - ch = line[j:j+1] - assert ch == in_string or in_string == '--' - j += 1 - i = j - concat.append(ch) - in_string = None - else: - if i < j: concat.append(line[i:j]); i = j - line = None - -class WindowsConsoleIOMixin(object): - # Ctrl+C handling with ReadFile() is messed up on Windows starting on Windows 8... here's some background reading: - # https://stackoverflow.com/a/43260436 - # https://github.com/microsoft/terminal/issues/334 - # We use ReadConsole when we can, so it doesn't affect us, but it's good info to know regardless. - def __init__(self, fd): - assert isatty(fd), "file descriptor must refer to a console (note that on Windows, NUL satisfies isatty(), but is not a console)" - self.fd = fd - self.handle = msvcrt.get_osfhandle(fd) - def fileno(self): return self.fd - def isatty(self): return isatty(self.fd) - def seekable(self): return False - def readable(self): return GetNumberOfConsoleInputEvents(self.handle, ctypes.byref(DWORD(0))) != 0 - def writable(self): n = DWORD(0); return WriteConsoleW(self.handle, ctypes.c_void_p(), n, ctypes.byref(n), ctypes.c_void_p()) != 0 - def readwcharsinto(self, buf, n): - nr = DWORD(n) - old_error = ctypes.get_last_error() - ctypes.set_last_error(0) - success = ReadConsoleW(self.handle, buf, nr, ctypes.byref(nr), ctypes.c_void_p()) - error = ctypes.get_last_error() - ctypes.set_last_error(old_error) - if not success: raise ctypes.WinError(error) - ERROR_OPERATION_ABORTED = 995 - if nr.value == 0 and error == ERROR_OPERATION_ABORTED: - # Apparently this can trigger pending KeyboardInterrupts? - time.sleep(1.0 / (1 << 64)) - raise KeyboardInterrupt() # If Python doesn't raise it, we can - return nr.value - def writewchars(self, buf, n): - nw = DWORD(n) - if not WriteConsoleW(self.handle, buf, nw, ctypes.byref(nw), ctypes.c_void_p()): - raise ctypes.WinError() - return nw.value - -class WindowsConsoleRawIO(WindowsConsoleIOMixin, io.RawIOBase): - def readinto(self, b): - wordsize = ctypes.sizeof(ctypes.c_wchar) - return self.readwcharsinto(getbuffer(b, True), len(b) // wordsize) * wordsize - def write(self, b): - wordsize = ctypes.sizeof(ctypes.c_wchar) - return self.writewchars(getbuffer(b, False), len(b) // wordsize) * wordsize - -class WindowsConsoleTextIO(WindowsConsoleIOMixin, io.TextIOBase): - buf = None - buffered = unicode() - translate = True - def getbuf(self, ncodeunits): - buf = self.buf - if buf is None or len(buf) < ncodeunits: - self.buf = buf = ctypes.create_unicode_buffer(ncodeunits) - return buf - @staticmethod # Don't let classes override this... they can override the caller instead - def do_read(self, nchars, translate_newlines): - prenewline = os.linesep[:-1] - newline = os.linesep[-1:] - empty = os.linesep[:0] - if nchars is None or nchars < -1: nchars = -1 - ncodeunits = nchars if nchars >= 0 else io.DEFAULT_BUFFER_SIZE # Unit mismatch, but doesn't matter; we'll loop - buf = None - istart = 0 - while True: - iend = self.buffered.find(newline, istart, min(istart + nchars, len(self.buffered)) if nchars >= 0 else None) if newline is not None else nchars - if iend >= 0: iend += len(newline) if newline is not None else 0 - if 0 <= iend <= len(self.buffered): - break - if buf is None: buf = self.getbuf(ncodeunits) - istart = len(self.buffered) - chunk = buf[:self.readwcharsinto(buf, ncodeunits)] - if translate_newlines: chunk = chunk.replace(prenewline, empty) - if chunk.startswith('\x1A'): # EOF on Windows (Ctrl+Z) at the beginning of a line results in the entire rest of the buffer being discarded - iend = istart - break - # Python 2 and Python 3 behaviors differ on Windows... Python 2's sys.stdin.readline() just deletes the next character if it sees EOF in the middle of a string! I won't emulate that here. - self.buffered += chunk # We're relying on Python's concatenation optimization here... we don't do it ourselves, since we want self.buffered to be valid every iteration in case there is an exception raised - result = self.buffered[:iend] - self.buffered = self.buffered[iend:] - return result - def read(self, nchars=-1): return WindowsConsoleTextIO.do_read(self, nchars, None, self.translate) - def readline(self, nchars=-1): return WindowsConsoleTextIO.do_read(self, nchars, self.translate) - def write(self, text): buf = ctypes.create_unicode_buffer(text); return self.writewchars(buf, max(len(buf) - 1, 0)) - -def wrap_windows_console_io(stream, is_output): - fd = None - if stream is not None and sys.version_info[0] < 3 and msvcrt and (is_output or pythonapi) and isatty(stream): - try: fd = stream.fileno() - except io.UnsupportedOperation: pass - result = stream - if fd is not None: - f = GetConsoleOutputCP if is_output else GetConsoleCP - if not f or f() != CP_UTF8: - try: - if True or is_output: - result = WindowsConsoleTextIO(fd) - else: - result = io.TextIOWrapper((io.BufferedWriter if is_output else io.BufferedReader)(WindowsConsoleRawIO(fd)), 'utf-16-le', 'strict', line_buffering=True) - except IOError: pass - return result - -class NonOwningTextIOWrapper(io.TextIOWrapper): - def __init__(self, base_textiowrapper, **kwargs): - assert isinstance(base_textiowrapper, io.TextIOWrapper) - self.base = base_textiowrapper # must keep a reference to this alive so it doesn't get closed - super(NonOwningTextIOWrapper, self).__init__(base_textiowrapper.buffer, **kwargs) - def close(self): - super(NonOwningTextIOWrapper, self).flush() - -def wrap_unicode_stdio(stream, is_writer, encoding): # The underlying stream must NOT be used directly until the stream returned by this function is disposed of - if isinstance(stream, io.TextIOWrapper): - stream.flush() # Make sure nothing is left in the buffer before we re-wrap it - none = object() - kwargs = {} - for key in ['encoding', 'errors', 'newline', 'line_buffering', 'write_through']: - value = getattr(stream, 'newlines' if key == 'newline' else key, none) - if value is not none: - kwargs[key] = value - kwargs['encoding'] = encoding - result = NonOwningTextIOWrapper(stream, **kwargs) - elif 'PYTHONIOENCODING' not in os.environ and str == bytes and stream in (sys.stdin, sys.stdout, sys.stderr): - result = (codecs.getwriter if is_writer else codecs.getreader)(encoding)(stream) - else: - result = stream - return result - -class StringEscapeParser(object): - def __init__(self): - import re - self.pattern = re.compile("\"((?:[^\"\\n]+|\\\\.)*)(?:\"|$)|\'([^\'\\n]*)(?:\'|$)|(\\S+)") - self.escape_pattern = re.compile("\\\\(.)", re.DOTALL) - @staticmethod - def escape_replacement(m): - text = m.group(1) - if text == "\\": text = "\\" - elif text == "/": text = "\n" - elif text == "n": text = "\n" - elif text == "r": text = "\r" - elif text == "t": text = "\t" - elif text == "v": text = "\v" - elif text == "f": text = "\f" - elif text == "a": text = "\a" - elif text == "b": text = "\b" - return text - def __call__(self, s): - escape_pattern = self.escape_pattern - escape_replacement = self.escape_replacement - result = [] - for match in self.pattern.finditer(s): - [m1, m2, m3] = match.groups() - if m1 is not None: result.append(escape_pattern.sub(escape_replacement, m1)) - if m2 is not None: result.append(m2) - if m3 is not None: result.append(escape_pattern.sub(escape_replacement, m3)) - return result - -class Database(object): - def __init__(self, name, *args, **kwargs): - self.connection = sqlite3.connect(name, *args, **kwargs) - self.cursor = self.connection.cursor() - self.name = name # assign name only AFTER cursor is created - -def isatty(file_or_fd): - result = True - method = getattr(file_or_fd, 'isatty', None) if not isinstance(file_or_fd, int) else None # this check is just an optimization - if method is not None: - try: tty = method() - except io.UnsupportedOperation: tty = None - result = result and tty is not None and tty - method = getattr(file_or_fd, 'fileno', None) if not isinstance(file_or_fd, int) else None # this check is just an optimization - if method is not None: - try: fd = method() - except io.UnsupportedOperation: fd = None - result = result and fd is not None and os.isatty(fd) and (not msvcrt or GetConsoleMode(msvcrt.get_osfhandle(fd), ctypes.byref(DWORD(0))) != 0) - return result - -def can_call_input_for_stdio(stream): - return stream == sys.stdin and sys.version_info[0] >= 3 - -class StdIOProxy(object): - # Maybe useful later: codecs.StreamRecoder(bytesIO, codec.decode, codec.encode, codec.streamwriter, codec.streamreader, errors='surrogateescape') - def __init__(self, stdin, stdout, stderr, codec, allow_set_code_page): - self.codec = codec - streams = (stdin, stdout, stderr) - for stream in streams: - assert isinstance(stream, io.IOBase) or sys.version_info[0] < 3 and isinstance(stream, file) or hasattr(stream, 'mode'), "unable to determine stream type" - assert not isinstance(stream, io.RawIOBase), "RAW I/O APIs are different and not supported" - self.streaminfos = tuple(map(lambda stream: - ( - stream, - isinstance(stream, io.BufferedIOBase) or isinstance(stream, io.RawIOBase) or not isinstance(stream, io.TextIOBase) and 'b' in stream.mode, - isinstance(stream, io.TextIOBase) or not (isinstance(stream, io.BufferedIOBase) or isinstance(stream, io.RawIOBase)) and 'b' not in stream.mode, - allow_set_code_page - ), - streams)) - @property - def stdin(self): return self.streaminfos[0][0] - @property - def stdout(self): return self.streaminfos[1][0] - @property - def stderr(self): return self.streaminfos[2][0] - def _coerce(self, streaminfo, codec, arg): - stream = streaminfo[0] - can_binary = streaminfo[1] - can_text = streaminfo[2] - if not isinstance(arg, bytes) and not isinstance(arg, buffer) and not isinstance(arg, unicode): - arg = unicode(arg) - if isinstance(arg, bytes) or isinstance(arg, buffer): - if not can_binary: - arg = codec.decode(arg, 'surrogateescape')[0] - elif isinstance(arg, unicode): - if not can_text: - arg = codec.encode(unicode(arg), 'strict')[0] - return arg - @staticmethod - def _do_readline(stream, allow_set_code_page, *args): - new_code_page = CP_UTF8 - old_code_page = GetConsoleCP() if msvcrt and GetConsoleCP and isatty(stream) else None - if old_code_page == new_code_page: old_code_page = None # Don't change code page if it's already correct... - if old_code_page is not None: - if not SetConsoleCP(new_code_page): - old_code_page = None - try: - result = stream.readline(*args) - finally: - if old_code_page is not None: - SetConsoleCP(old_code_page) - return result - @staticmethod - def _do_write(stream, allow_set_code_page, *args): - new_code_page = CP_UTF8 - old_code_page = GetConsoleOutputCP() if msvcrt and GetConsoleOutputCP and isatty(stream) else None - if old_code_page == new_code_page: old_code_page = None # Don't change code page if it's already correct... - if old_code_page is not None: - if not SetConsoleOutputCP(new_code_page): - old_code_page = None - try: - result = stream.write(*args) - finally: - if old_code_page is not None: - SetConsoleCP(old_code_page) - return result - def _readln(self, streaminfo, codec, prompt): - stream = streaminfo[0] - can_binary = streaminfo[1] - allow_set_code_page = streaminfo[3] - if can_call_input_for_stdio(stream) and not can_binary: # input() can't work with binary data - result = self._coerce(streaminfo, codec, "") - try: - result = input(*((self._coerce(streaminfo, codec, prompt),) if prompt is not None else ())) - result += self._coerce(streaminfo, codec, "\n") - except EOFError: pass - else: - self.output(*((prompt,) if prompt is not None else ())) - self.error() - result = StdIOProxy._do_readline(stream, allow_set_code_page) - return result - def _writeln(self, streaminfo, codec, *args, **kwargs): - stream = streaminfo[0] - allow_set_code_page = streaminfo[3] - flush = kwargs.pop('flush', True) - kwargs.setdefault('end', '\n') - kwargs.setdefault('sep', ' ') - end = kwargs.get('end') - sep = kwargs.get('sep') - first = True - for arg in args: - if first: first = False - elif sep is not None: - StdIOProxy._do_write(stream, allow_set_code_page, self._coerce(streaminfo, codec, sep)) - StdIOProxy._do_write(stream, allow_set_code_page, self._coerce(streaminfo, codec, arg)) - if end is not None: - StdIOProxy._do_write(stream, allow_set_code_page, self._coerce(streaminfo, codec, end)) - if flush: stream.flush() - def inputln(self, prompt=None): return self._readln(self.streaminfos[0], self.codec, prompt) - def output(self, *args, **kwargs): kwargs.setdefault('end', None); return self._writeln(self.streaminfos[1], self.codec, *args, **kwargs) - def outputln(self, *args, **kwargs): return self._writeln(self.streaminfos[1], self.codec, *args, **kwargs) - def error(self, *args, **kwargs): kwargs.setdefault('end', None); return self._writeln(self.streaminfos[2], self.codec, *args, **kwargs) - def errorln(self, *args, **kwargs): return self._writeln(self.streaminfos[2], self.codec, *args, **kwargs) - -class bytes_comparable_with_unicode(bytes): # For Python 2/3 compatibility, to allow implicit conversion between strings and bytes when it is safe. (Used for strings like literals which we know be safe.) - codec = codecs.lookup('ascii') # MUST be a safe encoding - @classmethod - def coerce(cls, other, for_output=False): - return cls.codec.encode(other)[0] if not isinstance(other, bytes) else bytes_comparable_with_unicode(other) if for_output else other - @classmethod - def translate_if_bytes(cls, value): - if value is not None and isinstance(value, bytes): value = cls(value) - return value - def __hash__(self): return super(bytes_comparable_with_unicode, self).__hash__() # To avoid warning - def __eq__(self, other): return super(bytes_comparable_with_unicode, self).__eq__(self.coerce(other)) - def __ne__(self, other): return super(bytes_comparable_with_unicode, self).__ne__(self.coerce(other)) - def __lt__(self, other): return super(bytes_comparable_with_unicode, self).__lt__(self.coerce(other)) - def __gt__(self, other): return super(bytes_comparable_with_unicode, self).__gt__(self.coerce(other)) - def __le__(self, other): return super(bytes_comparable_with_unicode, self).__le__(self.coerce(other)) - def __ge__(self, other): return super(bytes_comparable_with_unicode, self).__ge__(self.coerce(other)) - def __getitem__(self, index): return self.coerce(super(bytes_comparable_with_unicode, self).__getitem__(index), True) - def __add__(self, other): return self.coerce(super(bytes_comparable_with_unicode, self).__add__(self.coerce(other)), True) - def __iadd__(self, other): return self.coerce(super(bytes_comparable_with_unicode, self).__iadd__(self.coerce(other)), True) - def __radd__(self, other): return self.coerce(self.coerce(other).__add__(self), True) - def find(self, other, *args): return super(bytes_comparable_with_unicode, self).find(self.coerce(other), *args) - def join(self, others): return self.coerce(super(bytes_comparable_with_unicode, self).join(map(self.coerce, others)), True) - def startswith(self, other): return super(bytes_comparable_with_unicode, self).startswith(self.coerce(other)) - def __str__(self): return self.codec.decode(self)[0] - if str == bytes: - __unicode__ = __str__ - def __str__(self): raise NotImplementedError() - -def wrap_bytes_comparable_with_unicode_readline(readline): - def callback(*args): - line = readline(*args) - line = bytes_comparable_with_unicode.translate_if_bytes(line) - return line - return callback - -def main(program, *args, **kwargs): # **kwargs = dict(stdin=file, stdout=file, stderr=file); useful for callers who import this module - import argparse # slow import (compiles regexes etc.), so don't import it until needed - argparser = argparse.ArgumentParser( - prog=os.path.basename(program), - usage=None, - description=None, - epilog=None, - parents=[], - formatter_class=argparse.RawTextHelpFormatter) - argparser.add_argument('-version', '--version', action='store_true', help="show SQLite version") - argparser.add_argument('-batch', '--batch', action='store_true', help="force batch I/O") - argparser.add_argument('-init', '--init', metavar="FILE", help="read/process named file") - argparser.add_argument('filename', nargs='?', metavar="FILENAME", help="is the name of an SQLite database.\nA new database is created if the file does not previously exist.") - argparser.add_argument('sql', nargs='*', metavar="SQL", help="SQL commnds to execute after opening database") - argparser.add_argument('--readline', action='store', metavar="(true|false)", default="true", choices=("true", "false"), help="whether to import readline if available (default: %(default)s)") - argparser.add_argument('--self-test', action='store_true', help="perform a basic self-test") - argparser.add_argument('--cross-test', action='store_true', help="perform a basic test against the official executable") - argparser.add_argument('--unicode-stdio', action='store', metavar="(true|false)", default="true", choices=("true", "false"), help="whether to enable Unicode wrapper for standard I/O (default: %(default)s)") - argparser.add_argument('--console', action='store', metavar="(true|false)", default="true", choices=("true", "false"), help="whether to auto-detect and use console window APIs (default: %(default)s)") - argparser.add_argument('--encoding', default=ENCODING, help="the default encoding to use (default: %(default)s)") - (stdin, stdout, stderr) = (kwargs.pop('stdin', sys.stdin), kwargs.pop('stdout', sys.stdout), kwargs.pop('stderr', sys.stderr)) - parsed_args = argparser.parse_args(args) - codec = codecs.lookup(parsed_args.encoding or argparser.get_default('encoding')) - if parsed_args.self_test: self_test(codec) - if parsed_args.cross_test: cross_test("sqlite3", codec) - parse_escaped_strings = StringEscapeParser() - if parsed_args.unicode_stdio == "true": - stdin = wrap_unicode_stdio(stdin, False, codec.name) - stdout = wrap_unicode_stdio(stdout, True, codec.name) - stderr = wrap_unicode_stdio(stderr, True, codec.name) - if parsed_args.console == "true": - stdin = wrap_windows_console_io(stdin, False) - stdout = wrap_windows_console_io(stdout, True) - stderr = wrap_windows_console_io(stderr, True) - allow_set_code_page = sys.version_info[0] < 3 and False # This is only necessary on Python 2 if we use the default I/O functions instead of bypassing to ReadConsole()/WriteConsole() - stdio = StdIOProxy(stdin, stdout, stderr, codec, allow_set_code_page) - db = None - no_args = len(args) == 0 - init_sql = parsed_args.sql - is_nonpipe_input = stdin.isatty() # NOT the same thing as TTY! (NUL and /dev/null are the difference) - init_show_prompt = not parsed_args.batch and is_nonpipe_input - if not parsed_args.batch and isatty(stdin) and (parsed_args.readline == "true" or __name__ == '__main__') and parsed_args.readline != "false": - try: - with warnings.catch_warnings(): - warnings.filterwarnings('ignore', category=DeprecationWarning) - import readline - except ImportError: pass - if parsed_args and parsed_args.version: - stdio.outputln(sqlite3.sqlite_version); - else: - filename = parsed_args.filename - if filename is None: filename = ":memory:" - db = Database(filename, isolation_level=None) - def exec_script(db, filename, ignore_io_errors): - try: - with io.open(filename, 'r', encoding=codec.name) as f: # Assume .sql files are text -- any binary data inside them should be X'' encoded, not embedded directly - for command in sql_commands(wrap_bytes_comparable_with_unicode_readline(lambda *args: (lambda s: (s) or None)(f.readline()))): - result = exec_command(db, command, False and ignore_io_errors) - if result is not None: - return result - except IOError as ex: - stdio.errorln(ex) - if not ignore_io_errors: return ex.errno - def raise_invalid_command_error(command): - if isinstance(command, bytes): command = codec.decode(command)[0] - if command.startswith("."): command = command[1:] - raise RuntimeError("Error: unknown command or invalid arguments: \"%s\". Enter \".help\" for help" % (command.rstrip().replace("\\", "\\\\").replace("\"", "\\\""),)) - def exec_command(db, command, ignore_io_errors): - results = None - query = None - query_parameters = {} - try: - if command.startswith("."): - args = list(parse_escaped_strings(command)) - if args[0] in (".quit", ".exit"): - return 0 - elif args[0] == ".help": - stdio.error(""" -.cd DIRECTORY Change the working directory to DIRECTORY -.dump Dump the database in an SQL text format -.exit Exit this program -.help Show this message -.open FILE Close existing database and reopen FILE -.print STRING... Print literal STRING -.quit Exit this program -.read FILENAME Execute SQL in FILENAME -.schema ?PATTERN? Show the CREATE statements matching PATTERN -.show Show the current values for various settings -.tables ?TABLE? List names of tables -""".lstrip()) - elif args[0] == ".cd": - if len(args) != 2: raise_invalid_command_error(command) - os.chdir(args[1]) - elif args[0] == ".dump": - if len(args) != 1: raise_invalid_command_error(command) - foreign_keys = db.cursor.execute("PRAGMA foreign_keys;").fetchone()[0] - if foreign_keys in (0, "0", "off", "OFF"): - stdio.outputln("PRAGMA foreign_keys=OFF;", flush=False) - for line in db.connection.iterdump(): - stdio.outputln(line, flush=False) - stdio.output() - elif args[0] == ".open": - if len(args) <= 1: raise_invalid_command_error(command) - filename = args[-1] - for option in args[+1:-1]: - raise ValueError("option %s not supported" % (repr(option),)) - try: db.__init__(filename) - except sqlite3.OperationalError as ex: - ex.args = ex.args[:0] + ("Error: unable to open database \"%s\": %s" % (filename, ex.args[0]),) + ex.args[1:] - raise - elif args[0] == ".print": - stdio.outputln(*args[1:]) - elif args[0] == ".read": - if len(args) != 2: raise_invalid_command_error(command) - exec_script(db, args[1], ignore_io_errors) - elif args[0] == ".schema": - if len(args) > 2: raise_invalid_command_error(command) - pattern = args[1] if len(args) > 1 else None - query_parameters['type'] = 'table' - if pattern is not None: - query_parameters['pattern'] = pattern - query = "SELECT sql || ';' FROM sqlite_master WHERE type = :type" + (" AND name LIKE :pattern" if pattern is not None else "") + ";" - elif args[0] == ".show": - if len(args) > 2: raise_invalid_command_error(command) - stdio.errorln(" filename:", db.name) - elif args[0] == ".tables": - if len(args) > 2: raise_invalid_command_error(command) - pattern = args[1] if len(args) > 1 else None - query_parameters['type'] = 'table' - if pattern is not None: - query_parameters['pattern'] = pattern - query = "SELECT name FROM sqlite_master WHERE type = :type" + (" AND name LIKE :pattern" if pattern is not None else "") + ";" - else: - raise_invalid_command_error(args[0]) - else: - query = command - if query is not None: - results = db.cursor.execute(query if isinstance(query, unicode) else codec.decode(query, 'surrogatereplace')[0], query_parameters) - except (RuntimeError, OSError, FileNotFoundError, sqlite3.OperationalError) as ex: - stdio.errorln(exception_encode(ex, codec)) - if results is not None: - for row in results: - stdio.outputln(*tuple(map(lambda item: item if item is not None else "", row)), sep="|", flush=False) - stdio.output() - if db: - if parsed_args and parsed_args.init: - if is_nonpipe_input: stdio.errorln("-- Loading resources from", parsed_args.init) - exec_script(db, parsed_args.init, False) - def read_stdin(index, not_in_the_middle_of_any_input, prev_line): - show_prompt = init_show_prompt - to_write = [] - if index < len(init_sql): - line = init_sql[index] - if not line.startswith(".") and not line.rstrip().endswith(";"): - line += ";" - elif index == len(init_sql) and len(init_sql) > 0: - line = None - else: - if show_prompt: - if not_in_the_middle_of_any_input: - show_prompt = False - if index == 0: - to_write.append("SQLite version %s (adapter version %s)\nEnter \".help\" for usage hints.\n" % (sqlite3.sqlite_version, sqlite3.version)) - if no_args: - to_write.append("Connected to a transient in-memory database.\nUse \".open FILENAME\" to reopen on a persistent database.\n") - if index > 0 and not prev_line: - to_write.append("\n") - to_write.append("%7s " % ("sqlite%s>" % ("",) if not_in_the_middle_of_any_input else "...>",)) - try: - line = stdio.inputln("".join(to_write)) - except KeyboardInterrupt: - line = "" - raise # just kidding, don't handle it for now... - return line - for command in sql_commands(wrap_bytes_comparable_with_unicode_readline(read_stdin)): - result = exec_command(db, command, True) - if result is not None: - return result - if init_show_prompt and len(init_sql) == 0: - stdio.outputln() - -def call_program(cmdline, input_text): - import subprocess - return subprocess.Popen(cmdline, bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=False).communicate(input_text) - -def test_query(): - hexcodec = codecs.lookup('hex_codec') - ascii = 'ascii' - data1 = b"\xD8\xA2" - data2 = b"\x01\x02\xFF\x01\xFF\xFE\xFD" - values = [data1, data2] - query_bytes = b'SELECT %s;' % (b", ".join(map(lambda b: b"X'%s'" % (hexcodec.encode(b)[0].upper(),), values)),) - expected_bytes = b"%s\n" % (b"|".join(values),) - return query_bytes, expected_bytes - -def cross_test(sqlite_cmdline, codec): - (query_bytes, expected_bytes) = test_query() - (official_output, official_error) = call_program(sqlite_cmdline, query_bytes) - # We can't use os.linesep here since binaries may belong to different platforms (Win32/MinGW vs. MSYS/Cygwin vs. WSL...) - official_output = official_output.replace(b"\r\n", b"\n") - official_error = official_error.replace(b"\r\n", b"\n") - if official_output != expected_bytes: - raise sqlite3.ProgrammingError("expected bytes are wrong: official %s != expected %s" % (repr(official_output), repr(expected_bytes))) - if official_error: - raise sqlite3.ProgrammingError("did not expect errors from official binary") - -def self_test(codec): - (query_bytes, expected_bytes) = test_query() - if not (lambda stdin, stdout, stderr: not main(sys.argv[0], stdin=stdin, stdout=stdout, stderr=stderr) and stdout.getvalue() == expected_bytes)(io.BytesIO(query_bytes), io.BytesIO(), io.BytesIO()): - raise sqlite3.ProgrammingError("byte I/O is broken") - if not (lambda stdin, stdout, stderr: not main(sys.argv[0], stdin=stdin, stdout=stdout, stderr=stderr) and stdout.getvalue() == codec.decode(expected_bytes, 'surrogateescape'))(io.StringIO(query_bytes.decode(ascii)), io.StringIO(), io.StringIO()): - raise sqlite3.ProgrammingError("string I/O is broken") - -if __name__ == '__main__': - import sys - exit_code = main(*sys.argv) - if exit_code not in (None, 0): raise SystemExit(exit_code) diff --git a/hw/sol-hw07/tests/add-leaf.py b/hw/sol-hw07/tests/add-leaf.py deleted file mode 100644 index 6ac61c0602..0000000000 --- a/hw/sol-hw07/tests/add-leaf.py +++ /dev/null @@ -1,59 +0,0 @@ -test = { - 'name': 'add-leaf', - 'points': 1, - 'suites': [ - { - 'scored': True, - 'setup': """ - scm> (load-all ".") - scm> (define (tree label branches) (cons label branches)) - scm> (define (label t) (car t)) - scm> (define (branches t) (cdr t)) - scm> (define (is-leaf t) (null? (branches t))) - """, - 'type': 'scheme', - 'cases': [ - { - 'code': """ - scm> (define t (tree 1 nil)) - t - scm> (add-leaf t 10) - (1) - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> (define t (tree 1 (list (tree 2 nil) (tree 3 nil)))) - t - scm> (add-leaf t 4) ; if you're getting (1 (2) (3) 4), make sure you're appending 2 *lists of trees* together! - (1 (2) (3) (4)) - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> (define t (tree 1 (list (tree 2 (list (tree 3 nil) (tree 4 nil))) (tree 3 nil)))) - t - scm> (add-leaf t 5) - (1 (2 (3) (4) (5)) (3) (5)) - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> (define t (tree 1 (list (tree 2 (list (tree 4 nil) (tree 5 nil))) (tree 3 (list (tree 6 nil) (tree 7 nil)))))) - t - scm> (add-leaf t 8) - (1 (2 (4) (5) (8)) (3 (6) (7) (8)) (8)) - """, - 'hidden': False, - 'locked': False - } - ] - } - ] -} \ No newline at end of file diff --git a/hw/sol-hw07/tests/by_parent_height.py b/hw/sol-hw07/tests/by_parent_height.py deleted file mode 100644 index d834fb10d6..0000000000 --- a/hw/sol-hw07/tests/by_parent_height.py +++ /dev/null @@ -1,28 +0,0 @@ -test = { - 'name': 'by_parent_height', - 'points': 1, - 'suites': [ - { - 'type': 'sqlite', - 'ordered': True, - 'setup': """ - sqlite> .read hw07.sql - """, - 'cases': [ - { - 'locked': False, - 'code': r""" - sqlite> SELECT * FROM by_parent_height; - herbert - fillmore - abraham - delano - grover - barack - clinton - """, - }, - ], - }, - ] -} \ No newline at end of file diff --git a/hw/sol-hw07/tests/cadr-caddr.py b/hw/sol-hw07/tests/cadr-caddr.py deleted file mode 100644 index 5e9e817e46..0000000000 --- a/hw/sol-hw07/tests/cadr-caddr.py +++ /dev/null @@ -1,38 +0,0 @@ -test = { - 'name': 'cadr-caddr', - 'points': 1, - 'suites': [ - { - 'type': 'scheme', - 'setup': r""" - scm> (load-all ".") - """, - 'cases': [ - { - 'code': r""" - scm> (cddr '(1 2 3 4)) - (3 4) - """, - 'hidden': False, - 'locked': False - }, - { - 'code': r""" - scm> (cadr '(1 2 3 4)) - 2 - """, - 'hidden': False, - 'locked': False - }, - { - 'code': r""" - scm> (caddr '(1 2 3 4)) - 3 - """, - 'hidden': False, - 'locked': False - }, - ], - }, - ] -} \ No newline at end of file diff --git a/hw/sol-hw07/tests/low_variance.py b/hw/sol-hw07/tests/low_variance.py deleted file mode 100644 index 67c68793d0..0000000000 --- a/hw/sol-hw07/tests/low_variance.py +++ /dev/null @@ -1,21 +0,0 @@ -test = { - 'name': 'low_variance', - 'points': 1, - 'suites': [ - { - 'type': 'sqlite', - 'setup': """ - sqlite> .read hw07.sql - """, - 'cases': [ - { - 'locked': False, - 'code': r""" - sqlite> SELECT * FROM low_variance; - curly|1 - """, - }, - ], - }, - ] -} \ No newline at end of file diff --git a/hw/sol-hw07/tests/pow.py b/hw/sol-hw07/tests/pow.py deleted file mode 100644 index 67d7398ed6..0000000000 --- a/hw/sol-hw07/tests/pow.py +++ /dev/null @@ -1,47 +0,0 @@ -test = { - 'name': 'pow', - 'points': 1, - 'suites': [ - { - 'scored': True, - 'setup': """ - scm> (load-all ".") - """, - 'type': 'scheme', - 'cases': [ - { - 'code': """ - scm> (pow 2 5) - 32 - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> (pow 10 3) - 1000 - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> (pow 3 3) - 27 - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> (pow 1 100000000000000) ; make sure this doesn't run forever! - 1 - """, - 'hidden': False, - 'locked': False - }, - ], - }, - ] -} \ No newline at end of file diff --git a/hw/sol-hw07/tests/repeatedly-cube.py b/hw/sol-hw07/tests/repeatedly-cube.py deleted file mode 100644 index cad91f867a..0000000000 --- a/hw/sol-hw07/tests/repeatedly-cube.py +++ /dev/null @@ -1,27 +0,0 @@ -test = { - 'name': 'repeatedly-cube', - 'points': 1, - 'suites': [ - { - 'type': 'scheme', - 'scored': True, - 'setup': """ - scm> (load-all ".") - """, - 'cases': [ - { - 'code': """ - scm> (repeatedly-cube 3 1) - 1 - scm> (repeatedly-cube 2 2) - 512 - scm> (repeatedly-cube 3 2) - 134217728 - """, - 'hidden': False, - 'locked' : False - }, - ] - } - ] -} \ No newline at end of file diff --git a/hw/sol-hw07/tests/sentences.py b/hw/sol-hw07/tests/sentences.py deleted file mode 100644 index b9f8e53968..0000000000 --- a/hw/sol-hw07/tests/sentences.py +++ /dev/null @@ -1,22 +0,0 @@ -test = { - 'name': 'sentences', - 'points': 1, - 'suites': [ - { - 'type': 'sqlite', - 'setup': """ - sqlite> .read hw07.sql - """, - 'cases': [ - { - 'locked': False, - 'code': r""" - 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 - """, - }, - ], - }, - ] -} \ No newline at end of file diff --git a/hw/sol-hw07/tests/size_of_dogs.py b/hw/sol-hw07/tests/size_of_dogs.py deleted file mode 100644 index 3039e8e017..0000000000 --- a/hw/sol-hw07/tests/size_of_dogs.py +++ /dev/null @@ -1,25 +0,0 @@ -test = { - 'name': 'size_of_dogs', - 'points': 1, - 'suites': [ - { - 'type': 'sqlite', - 'setup': """ - sqlite> .read hw07.sql - """, - 'cases': [ - { - 'locked': False, - 'code': r""" - sqlite> SELECT name FROM size_of_dogs WHERE size="toy" OR size="mini"; - abraham - eisenhower - fillmore - grover - herbert - """, - }, - ], - }, - ] -} \ No newline at end of file diff --git a/hw/sol-hw08/assets/after.png b/hw/sol-hw08/assets/after.png deleted file mode 100644 index cf8e95e510..0000000000 Binary files a/hw/sol-hw08/assets/after.png and /dev/null differ diff --git a/hw/sol-hw08/assets/before.png b/hw/sol-hw08/assets/before.png deleted file mode 100644 index 6da4249e85..0000000000 Binary files a/hw/sol-hw08/assets/before.png and /dev/null differ diff --git a/hw/sol-hw08/hw08.ok b/hw/sol-hw08/hw08.ok deleted file mode 100644 index 1c2530d20c..0000000000 --- a/hw/sol-hw08/hw08.ok +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "Homework 8", - "endpoint": "cal/cs61a/fa24/hw08", - "src": [ - "hw08.scm" - ], - "tests": { - "hw*.py": "doctest", - "tests/*.py": "ok_test" - }, - "default_tests": [ - "ascending", - "filter", - "interleave", - "no_repeats" - ], - "protocols": [ - "restore", - "file_contents", - "grading", - "analytics", - "help", - "unlock", - "backup" - ] -} \ No newline at end of file diff --git a/hw/sol-hw08/hw08.py b/hw/sol-hw08/hw08.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/hw/sol-hw08/hw08.scm b/hw/sol-hw08/hw08.scm deleted file mode 100644 index 9138fe2c11..0000000000 --- a/hw/sol-hw08/hw08.scm +++ /dev/null @@ -1,36 +0,0 @@ -(define (ascending? s) - (if (or (null? s) (null? (cdr s))) - #t - (and (<= (car s) (car (cdr s))) - (ascending? (cdr s))))) - -(define (my-filter pred s) - (cond - ((null? s) - '()) - ((pred (car s)) - (cons (car s) (my-filter pred (cdr s)))) - (else - (my-filter pred (cdr s))))) - -(define (interleave lst1 lst2) - (if (or (null? lst1) (null? lst2)) - (append lst1 lst2) - (cons (car lst1) - (cons (car lst2) - (interleave (cdr lst1) (cdr lst2))))) - ; Alternate Solution - (cond - ((null? lst1) - lst2) - ((null? lst2) - lst1) - (else - (cons (car lst1) (interleave lst2 (cdr lst1)))))) - -(define (no-repeats s) - (if (null? s) - s - (cons (car s) - (no-repeats - (filter (lambda (x) (not (= (car s) x))) (cdr s)))))) diff --git a/hw/sol-hw08/hw08.zip b/hw/sol-hw08/hw08.zip deleted file mode 100644 index 42471fc663..0000000000 Binary files a/hw/sol-hw08/hw08.zip and /dev/null differ diff --git a/hw/sol-hw08/index.html b/hw/sol-hw08/index.html deleted file mode 100644 index 624a8bbcec..0000000000 --- a/hw/sol-hw08/index.html +++ /dev/null @@ -1,568 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Homework 8 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Homework 8 Solutions - - - - - - -

-
- -

Solution Files

-

You can find the solutions in hw08.scm.

- - -

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.

- - - - -

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.

- - -

YouTube link

-
- - -

Q1: Ascending

- - -

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)
-
(if (or (null? s) (null? (cdr s))) - #t - (and (<= (car s) (car (cdr s))) (ascending? (cdr s))))
)
- - - -
- -

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!

- - - -
(define (my-filter pred s)
-
(cond ((null? s) '()) - ((pred (car s)) (cons (car s) (my-filter pred (cdr s)))) - (else (my-filter pred (cdr s)))) -
)
- - -
- -

- - -

YouTube link

- -

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.

- - - -

-    (define (interleave lst1 lst2)
-
(if (or (null? lst1) (null? lst2)) - (append lst1 lst2) - (cons (car lst1) - (cons (car lst2) - (interleave (cdr lst1) (cdr lst2))))) - - ; Alternate Solution - (cond - ((null? lst1) lst2) - ((null? lst2) lst1) - (else (cons (car lst1) (interleave lst2 (cdr lst1)))) - )
)
- - -
- -

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)).

- - - -
(define (no-repeats s)
-
(if (null? s) s - (cons (car s) - (no-repeats (filter (lambda (x) (not (= (car s) x))) (cdr s)))))
)
- - -
- -

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.

- -
    -
  1. Fall 2022 Final, Question 8: A Parentheses Scheme
  2. -
  3. Spring 2022 Final, Question 11: Beadazzled, The Scheme-quel
  4. -
  5. Fall 2021 Final, Question 4: Spice
  6. -
- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/hw/sol-hw08/ok b/hw/sol-hw08/ok deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/hw/sol-hw08/ok and /dev/null differ diff --git a/hw/sol-hw08/scheme b/hw/sol-hw08/scheme deleted file mode 100644 index 2174145cff..0000000000 Binary files a/hw/sol-hw08/scheme and /dev/null differ diff --git a/hw/sol-hw08/tests/ascending.py b/hw/sol-hw08/tests/ascending.py deleted file mode 100644 index 559bdf7b13..0000000000 --- a/hw/sol-hw08/tests/ascending.py +++ /dev/null @@ -1,44 +0,0 @@ -test = { - 'name': 'ascending?', - 'points': 1, - 'suites': [ - { - 'type': 'scheme', - 'setup': r""" - scm> (load-all ".") - """, - 'cases': [ - { - 'code': r""" - scm> (ascending? '(1 2 3 4 5)) ; #t or #f - #t - """, - }, - { - 'code': r""" - scm> (ascending? '(1 5 2 4 3)) ; #t or #f - #f - """, - }, - { - 'code': r""" - scm> (ascending? '(2 2)) ; #t or #f - #t - """, - }, - { - 'code': r""" - scm> (ascending? '(1 2 2 4 3)) ; #t or #f - #f - """, - }, - { - 'code': r""" - scm> (ascending? '()) ; #t or #f - #t - """, - }, - ], - }, - ] -} \ No newline at end of file diff --git a/hw/sol-hw08/tests/filter.py b/hw/sol-hw08/tests/filter.py deleted file mode 100644 index 84f092a106..0000000000 --- a/hw/sol-hw08/tests/filter.py +++ /dev/null @@ -1,67 +0,0 @@ -test = { - 'name': 'my-filter', - 'points': 1, - 'suites': [ - { - 'scored': True, - 'setup': """ - scm> (load-all ".") - """, - 'type': 'scheme', - 'cases': [ - { - 'code': """ - scm> (my-filter even? '(1 2 3 4)) - (2 4) - """, - 'hidden': False - }, - { - 'code': """ - scm> (my-filter odd? '(1 3 5)) - (1 3 5) - """, - 'hidden': False - }, - { - 'code': """ - scm> (my-filter odd? '(2 4 6 1)) - (1) - """, - 'hidden': False - }, - { - 'code': """ - scm> (my-filter even? '(3)) - () - """, - 'hidden': False - }, - { - 'code': """ - scm> (my-filter odd? nil) - () - """, - 'hidden': False - }, - ] - }, - { - 'scored': True, - 'setup': """ - scm> (define filter nil) - scm> (load-all ".") - """, - 'type': 'scheme', - 'cases': [ - { - 'code': """ - scm> (my-filter even? '(1 2 3 4)) ; checks you dont use builtin filter - (2 4) - """, - 'hidden': False - } - ] - } - ] -} \ No newline at end of file diff --git a/hw/sol-hw08/tests/interleave.py b/hw/sol-hw08/tests/interleave.py deleted file mode 100644 index 2ded432254..0000000000 --- a/hw/sol-hw08/tests/interleave.py +++ /dev/null @@ -1,70 +0,0 @@ -test = { - 'name': 'interleave', - 'points': 1, - 'suites': [ - { - 'scored': True, - 'setup': """ - scm> (load-all ".") - """, - 'type': 'scheme', - 'cases': [ - { - 'code': """ - scm> (interleave (list 1 3 5) (list 2 4)) - (1 2 3 4 5) - """, - 'hidden': False - }, - { - 'code': """ - scm> (interleave (list 2 4) (list 1 3 5)) - (2 1 4 3 5) - """, - 'hidden': False - }, - { - 'code': """ - scm> (interleave (list 1 2) (list 1 2)) - (1 1 2 2) - """, - 'hidden': False - }, - { - 'code': """ - scm> (interleave '(1 2 3 4 5 6) '(7 8)) - (1 7 2 8 3 4 5 6) - """, - 'hidden': False - }, - ] - }, - { - 'scored': True, - 'setup': """ - scm> (load-all ".") - """, - 'type': 'scheme', - 'cases': [ - { - 'code': """ - scm> (interleave (list 1 3 5) (list 2 4 6)) - (1 2 3 4 5 6) - """, - 'hidden': False - }, - { - 'code': """ - scm> (interleave (list 1 3 5) nil) - (1 3 5) - scm> (interleave nil (list 1 3 5)) - (1 3 5) - scm> (interleave nil nil) - () - """, - 'hidden': False - }, - ] - } - ] -} \ No newline at end of file diff --git a/hw/sol-hw08/tests/no_repeats.py b/hw/sol-hw08/tests/no_repeats.py deleted file mode 100644 index 477c7bc58b..0000000000 --- a/hw/sol-hw08/tests/no_repeats.py +++ /dev/null @@ -1,84 +0,0 @@ -test = { - 'name': 'no-repeats', - 'points': 1, - 'suites': [ - { - 'scored': True, - 'setup': """ - scm> (load-all ".") - """, - 'type': 'scheme', - 'cases': [ - { - 'code': """ - scm> (no-repeats '(5 4 3 2 1)) - (5 4 3 2 1) - """, - 'hidden': False, - "locked": False - }, - { - 'code': """ - scm> (no-repeats '(5 4 3 2 1 1)) - (5 4 3 2 1) - """, - 'hidden': False, - "locked": False - }, - { - 'code': """ - scm> (no-repeats '(5 5 4 3 2 1)) - (5 4 3 2 1) - """, - 'hidden': False, - "locked": False - }, - { - 'code': """ - scm> (no-repeats '(12)) - (12) - """, - 'hidden': False, - "locked": False - }, - { - 'code': """ - scm> (no-repeats '(1 1 1 1 1 1)) - (1) - """, - 'hidden': False, - "locked": False - }, - ] - }, - { - 'scored': True, - 'setup': """ - scm> (load-all ".") - """, - 'type': 'scheme', - 'cases': [ - { - 'code': """ - scm> (no-repeats (list 5 4 2)) - (5 4 2) - """, - 'hidden': False, - "locked": False - }, - { - 'code': """ - scm> (no-repeats (list 5 4 5 4 2 2)) - (5 4 2) - scm> (no-repeats (list 5 5 5 5 5)) - (5) - scm> (no-repeats ()) - () - """, - 'hidden': False, - "locked": False - }, - ] - } - ] -} \ No newline at end of file diff --git a/hw/sol-hw09/assets/after.png b/hw/sol-hw09/assets/after.png deleted file mode 100644 index cf8e95e510..0000000000 Binary files a/hw/sol-hw09/assets/after.png and /dev/null differ diff --git a/hw/sol-hw09/assets/before.png b/hw/sol-hw09/assets/before.png deleted file mode 100644 index 6da4249e85..0000000000 Binary files a/hw/sol-hw09/assets/before.png and /dev/null differ diff --git a/hw/sol-hw09/hw09.ok b/hw/sol-hw09/hw09.ok deleted file mode 100644 index 80a70086cb..0000000000 --- a/hw/sol-hw09/hw09.ok +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "Homework 9", - "endpoint": "cal/cs61a/fa24/hw09", - "src": [ - "hw09.scm" - ], - "tests": { - "hw*.py": "doctest", - "tests/*.py": "ok_test" - }, - "default_tests": [ - "curry-cook", - "curry-consume", - "switch-to-cond" - ], - "protocols": [ - "restore", - "file_contents", - "grading", - "analytics", - "help", - "unlock", - "backup" - ] -} \ No newline at end of file diff --git a/hw/sol-hw09/hw09.scm b/hw/sol-hw09/hw09.scm deleted file mode 100644 index 50d2132756..0000000000 --- a/hw/sol-hw09/hw09.scm +++ /dev/null @@ -1,21 +0,0 @@ -(define (curry-cook formals body) - (if (null? formals) - body - `(lambda (,(car formals)) - ,(curry-cook (cdr formals) body)))) - -(define (curry-consume curry args) - (if (null? args) - curry - (curry-consume (curry (car args)) (cdr args)))) - -(define-macro (switch expr options) - (switch-to-cond (list 'switch expr options))) - -(define (switch-to-cond switch-expr) - (cons 'cond - (map (lambda (option) - (cons - `(equal? ,(car (cdr switch-expr)) ,(car option)) - (cdr option))) - (car (cdr (cdr switch-expr)))))) diff --git a/hw/sol-hw09/hw09.zip b/hw/sol-hw09/hw09.zip deleted file mode 100644 index 955176e14d..0000000000 Binary files a/hw/sol-hw09/hw09.zip and /dev/null differ diff --git a/hw/sol-hw09/index.html b/hw/sol-hw09/index.html deleted file mode 100644 index ba345b1914..0000000000 --- a/hw/sol-hw09/index.html +++ /dev/null @@ -1,510 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Homework 9 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Homework 9 Solutions - - - - - - -

-
- -

Solution Files

-

You can find the solutions in hw09.scm.

- - -

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.

- - - - -

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.

- - -

YouTube link

- -
- - -

Programs as Data: Chef Curry

- - -

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)))
- - - - - -
(define (curry-cook formals body)
-
(if (null? formals) - body - `(lambda (,(car formals)) ,(curry-cook (cdr formals) body)) - )
)
- - -
- -

Use Ok to test your code:

python3 ok -q curry-cook
- -
- - -

Q2: Consuming Curry

- - - - -

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:

- -
    -
  1. If curry is an n-curried function, then there will be at most n arguments in args.
  2. -
  3. 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.
  4. -
- -

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
- - - - - -
(define (curry-consume curry args)
-
(if (null? args) - curry - (curry-consume (curry (car args)) (cdr args)) - )
)
- - -
- -

Use Ok to test your code:

python3 ok -q curry-consume
- -
- - -

Macros

- - -

Q3: Switch to Cond

- - -

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:

- -
scm> (define-macro (switch expr options)
-                   (switch-to-cond (list 'switch expr options))
-     )
- - -
- -

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.

- -
scm> (switch-to-cond `(switch (+ 1 1) ((1 2) (2 4) (3 6))))
-(cond ((equal? (+ 1 1) 1) 2) ((equal? (+ 1 1) 2) 4) ((equal? (+ 1 1) 3) 6))
- - - - - -
(define-macro (switch expr options) (switch-to-cond (list 'switch expr options)))
-
-(define (switch-to-cond switch-expr)
-
(cons 'cond
(map -
(lambda (option) (cons `(equal? ,(car (cdr switch-expr)) ,(car option)) (cdr option)))
(car (cdr (cdr switch-expr))))))
- - -
- -

Use Ok to test your code:

python3 ok -q switch-to-cond
- -
- - -

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!

- -

Macros

- -
    -
  1. Fall 2019 Final Q9: Macro Lens
  2. -
  3. Summer 2019 Final Q10c: Slice
  4. -
  5. Spring 2019 Final Q8: Macros
  6. -
- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/hw/sol-hw09/ok b/hw/sol-hw09/ok deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/hw/sol-hw09/ok and /dev/null differ diff --git a/hw/sol-hw09/scheme b/hw/sol-hw09/scheme deleted file mode 100644 index 2174145cff..0000000000 Binary files a/hw/sol-hw09/scheme and /dev/null differ diff --git a/hw/sol-hw09/tests/curry-consume.py b/hw/sol-hw09/tests/curry-consume.py deleted file mode 100644 index 9c0b3c8764..0000000000 --- a/hw/sol-hw09/tests/curry-consume.py +++ /dev/null @@ -1,34 +0,0 @@ -test = { - 'name': 'curry-consume', - 'points': 1, - 'suites': [ - { - 'type': 'scheme', - 'setup': r""" - scm> (load-all ".") - """, - 'cases': [ - { - 'code': r""" - scm> (define three-curry (curry-cook '(x y z) '(+ x (* y z)))) - three-curry - scm> three-curry - (lambda (x) (lambda (y) (lambda (z) (+ x (* y z))))) - scm> (define three-curry-fn (eval three-curry)) ; three-curry-fn is a lambda function derived from the program - three-curry-fn - scm> (define eat-two (curry-consume three-curry-fn '(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-fn '(1 2 3)) ; all three arguments at once! - 7 - """, - "locked": False, - "hidden": False - }, - ], - }, - ] -} \ No newline at end of file diff --git a/hw/sol-hw09/tests/curry-cook.py b/hw/sol-hw09/tests/curry-cook.py deleted file mode 100644 index a2eb6a971c..0000000000 --- a/hw/sol-hw09/tests/curry-cook.py +++ /dev/null @@ -1,30 +0,0 @@ -test = { - 'name': 'curry-cook', - 'points': 1, - 'suites': [ - { - 'type': 'scheme', - 'setup': r""" - scm> (load-all ".") - """, - 'cases': [ - { - 'code': r""" - scm> (curry-cook '(a) 'a) - (lambda (a) a) - scm> (curry-cook '(x y) '(+ x y)) - (lambda (x) (lambda (y) (+ x y))) - scm> (define adder (curry-cook '(x y) '(+ x y))) - adder - scm> (eval adder) - (lambda (x) (lambda (y) (+ x y))) - scm> (((eval adder) 2) 3) - 5 - """, - "locked": False, - "hidden": False - }, - ], - }, - ] -} \ No newline at end of file diff --git a/hw/sol-hw09/tests/switch-to-cond.py b/hw/sol-hw09/tests/switch-to-cond.py deleted file mode 100644 index 71c7a987f2..0000000000 --- a/hw/sol-hw09/tests/switch-to-cond.py +++ /dev/null @@ -1,38 +0,0 @@ -test = { - 'name': 'switch-to-cond', - 'points': 1, - 'suites': [ - { - 'type': 'scheme', - 'scored': True, - 'cases': [ - { - 'code': r""" - scm> (switch-to-cond `(switch (+ 1 1) ((1 2) - .... (2 4) - .... (3 6)))) - (cond ((equal? (+ 1 1) 1) 2) ((equal? (+ 1 1) 2) 4) ((equal? (+ 1 1) 3) 6)) - scm> (switch 1 ((1 (print 'a)) - .... (2 (print 'b)) - .... (3 (print 'c)))) - a - scm> (switch (+ 1 1) ((1 (print 'a)) - .... (2 (print 'b)) - .... (3 (print 'c)))) - b - scm> (define x 'b) - x - scm> (switch x (('a (print 1)) - .... ('b (print 2)) - .... ('c (print 3)))) - 2 - """, - 'locked' : False - }, - ], - 'setup': r""" - scm> (load-all ".") - """, - }, - ] -} \ No newline at end of file diff --git a/hw/sol-hw10/data.sql b/hw/sol-hw10/data.sql deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/hw/sol-hw10/hw10.ok b/hw/sol-hw10/hw10.ok deleted file mode 100644 index 8959ce5fff..0000000000 --- a/hw/sol-hw10/hw10.ok +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "Homework 10", - "endpoint": "cal/cs61a/fa24/hw10", - "src": [ - "hw10.sql", - "hw10.py" - ], - "tests": { - "hw*.py": "doctest", - "tests/*.py": "ok_test" - }, - "default_tests": [ - "by_parent_height", - "size_of_dogs", - "sentences", - "low_variance" - ], - "protocols": [ - "restore", - "file_contents", - "unlock", - "grading", - "analytics", - "help", - "backup" - ] -} \ No newline at end of file diff --git a/hw/sol-hw10/hw10.sql b/hw/sol-hw10/hw10.sql deleted file mode 100644 index c553724d65..0000000000 --- a/hw/sol-hw10/hw10.sql +++ /dev/null @@ -1,54 +0,0 @@ -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; - - --- 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; - - --- The size of each dog -CREATE TABLE size_of_dogs AS - SELECT name, size FROM dogs, sizes - WHERE height > min AND height <= max; - - --- 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; - - --- 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); - diff --git a/hw/sol-hw10/hw10.zip b/hw/sol-hw10/hw10.zip deleted file mode 100644 index 0c59c10b55..0000000000 Binary files a/hw/sol-hw10/hw10.zip and /dev/null differ diff --git a/hw/sol-hw10/index.html b/hw/sol-hw10/index.html deleted file mode 100644 index d77f316f99..0000000000 --- a/hw/sol-hw10/index.html +++ /dev/null @@ -1,598 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Homework 10 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Homework 10 Solutions - - - - - - -

-
- -

Solution Files

-

You can find the solutions in the hw10.sql file.

- - -
- -

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.

- - -

YouTube link

- -
- - -

SQL

- - - -

Dog Data

- - -

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.

- -
sqlite> SELECT * FROM by_parent_height;
-herbert
-fillmore
-abraham
-delano
-grover
-barack
-clinton
- -

Use Ok to test your code:

python3 ok -q by_parent_height
- -
- - -
- -

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;
- -

The output should look like the following:

- -
sqlite> SELECT * FROM size_of_dogs;
-abraham|toy
-barack|standard
-clinton|standard
-delano|standard
-eisenhower|mini
-fillmore|mini
-grover|toy
-herbert|mini
- -

Use Ok to test your code:

python3 ok -q size_of_dogs
- -
- - - -
- -

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);
--- 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.

- -
    -
  1. Fall 2019 Final, Question 10: Big Game
  2. -
  3. Summer 2019 Final, Question 8: The Big SQL
  4. -
  5. Fall 2018 Final, Question 7: SQL of Course
  6. -
- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/hw/sol-hw10/ok b/hw/sol-hw10/ok deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/hw/sol-hw10/ok and /dev/null differ diff --git a/hw/sol-hw10/sqlite_shell.py b/hw/sol-hw10/sqlite_shell.py deleted file mode 100644 index cf1d3686fc..0000000000 --- a/hw/sol-hw10/sqlite_shell.py +++ /dev/null @@ -1,681 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the MIT license - -# A simple SQLite shell that uses the built-in Python adapter. - -import codecs -import io -import os -import sys -import sqlite3 -import time -import warnings - -try: FileNotFoundError -except NameError: FileNotFoundError = OSError - -if str != bytes: buffer = bytes -if str != bytes: unicode = str - -try: import msvcrt -except ImportError: msvcrt = None - -CP_UTF8 = 65001 -pythonapi = None -if msvcrt: - import ctypes - (BOOL, DWORD, HANDLE, UINT) = (ctypes.c_long, ctypes.c_ulong, ctypes.c_void_p, ctypes.c_uint) - GetConsoleCP = ctypes.WINFUNCTYPE(UINT)(('GetConsoleCP', ctypes.windll.kernel32)) - SetConsoleCP = ctypes.WINFUNCTYPE(BOOL, UINT)(('SetConsoleCP', ctypes.windll.kernel32)) - GetConsoleOutputCP = ctypes.WINFUNCTYPE(UINT)(('GetConsoleOutputCP', ctypes.windll.kernel32)) - SetConsoleOutputCP = ctypes.WINFUNCTYPE(BOOL, UINT)(('SetConsoleOutputCP', ctypes.windll.kernel32)) - GetConsoleMode = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.POINTER(DWORD), use_last_error=True)(('GetConsoleMode', ctypes.windll.kernel32)) - GetNumberOfConsoleInputEvents = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.POINTER(DWORD), use_last_error=True)(('GetNumberOfConsoleInputEvents', ctypes.windll.kernel32)) - ReadConsoleW = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.c_void_p, DWORD, ctypes.POINTER(DWORD), ctypes.c_void_p, use_last_error=True)(('ReadConsoleW', ctypes.windll.kernel32)) - WriteConsoleW = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.c_void_p, DWORD, ctypes.POINTER(DWORD), ctypes.c_void_p, use_last_error=True)(('WriteConsoleW', ctypes.windll.kernel32)) - class Py_buffer(ctypes.Structure): _fields_ = [('buf', ctypes.c_void_p), ('obj', ctypes.py_object), ('len', ctypes.c_ssize_t), ('itemsize', ctypes.c_ssize_t), ('readonly', ctypes.c_int), ('ndim', ctypes.c_int), ('format', ctypes.c_char_p), ('shape', ctypes.POINTER(ctypes.c_ssize_t)), ('strides', ctypes.POINTER(ctypes.c_ssize_t)), ('suboffsets', ctypes.POINTER(ctypes.c_ssize_t))] + ([('smalltable', ctypes.c_ssize_t * 2)] if sys.version_info[0] <= 2 else []) + [('internal', ctypes.c_void_p)] - try: from ctypes import pythonapi - except ImportError: pass -if pythonapi: - def getbuffer(b, writable): - arr = Py_buffer() - pythonapi.PyObject_GetBuffer(ctypes.py_object(b), ctypes.byref(arr), ctypes.c_int(writable)) - try: buf = (ctypes.c_ubyte * arr.len).from_address(arr.buf) - finally: pythonapi.PyBuffer_Release(ctypes.byref(arr)) - return buf - -ENCODING = 'utf-8' - -if sys.version_info[0] < 3: - class NotASurrogateError(Exception): pass - def surrogateescape_handler(exc): - # Source: https://github.com/PythonCharmers/python-future/blob/aef57391c0cd58bf840dff5e2bc2c8c0f5b0a1b4/src/future/utils/surrogateescape.py - mystring = exc.object[exc.start:exc.end] - try: - if isinstance(exc, UnicodeDecodeError): - decoded = [] - for ch in mystring: - if isinstance(ch, int): - code = ch - else: - code = ord(ch) - if 0x80 <= code <= 0xFF: - decoded.append(unichr(0xDC00 + code)) - elif code <= 0x7F: - decoded.append(unichr(code)) - else: - raise NotASurrogateError() - decoded = str().join(decoded) - elif isinstance(exc, UnicodeEncodeError): - decoded = [] - for ch in mystring: - code = ord(ch) - if not 0xD800 <= code <= 0xDCFF: - raise NotASurrogateError() - if 0xDC00 <= code <= 0xDC7F: - decoded.append(unichr(code - 0xDC00)) - elif code <= 0xDCFF: - decoded.append(unichr(code - 0xDC00)) - else: - raise NotASurrogateError() - decoded = str().join(decoded) - else: - raise exc - except NotASurrogateError: - raise exc - return (decoded, exc.end) - codecs.register_error('surrogateescape', surrogateescape_handler) - -def exception_encode(ex, codec): - if str == bytes: - reduced = ex.__reduce__() - ex = reduced[0](*tuple(map(lambda arg: codec.decode(arg)[0] if isinstance(arg, bytes) else arg, reduced[1]))) - return ex - -def sql_commands(read_line): - delims = ['"', "'", ';', '--'] - counter = 0 - in_string = None - j = i = 0 - prev_line = None - line = None - concat = [] - while True: - if line is None: - while True: # process preprocessor directives - counter += 1 - not_in_the_middle_of_any_input = not in_string and i == j and all(map(lambda chunk_: len(chunk_) == 0, concat)) - line = read_line(counter - 1, not_in_the_middle_of_any_input, prev_line) - empty_string = line[:0] if line is not None else line - prev_line = line - if not line: - break - if not_in_the_middle_of_any_input and line.startswith("."): - yield line - line = None - else: - break - if not line: - break - j = i = 0 - if j < len(line): - (j, delim) = min(map(lambda pair: pair if pair[0] >= 0 else (len(line), pair[1]), map(lambda d: (line.find(d, j), d), in_string or delims if in_string != '--' else "\n"))) - if i < j: concat.append(line[i:j]); i = j - if not in_string: - if j < len(line): - j += len(delim) - if delim == ';': - i = j - concat.append(line[j : j + len(delim)]) # ensure delimeter is the same type as the string (it may not be due to implicit conversion) - # Eat up any further spaces until a newline - while j < len(line): - delim = line[j:j+1] - if not delim.isspace(): break - j += 1 - if delim == "\n": break - if i < j: concat.append(line[i:j]); i = j - yield empty_string.join(concat) - del concat[:] - else: - in_string = delim - else: - if j < len(line): - ch = line[j:j+1] - assert ch == in_string or in_string == '--' - j += 1 - i = j - concat.append(ch) - in_string = None - else: - if i < j: concat.append(line[i:j]); i = j - line = None - -class WindowsConsoleIOMixin(object): - # Ctrl+C handling with ReadFile() is messed up on Windows starting on Windows 8... here's some background reading: - # https://stackoverflow.com/a/43260436 - # https://github.com/microsoft/terminal/issues/334 - # We use ReadConsole when we can, so it doesn't affect us, but it's good info to know regardless. - def __init__(self, fd): - assert isatty(fd), "file descriptor must refer to a console (note that on Windows, NUL satisfies isatty(), but is not a console)" - self.fd = fd - self.handle = msvcrt.get_osfhandle(fd) - def fileno(self): return self.fd - def isatty(self): return isatty(self.fd) - def seekable(self): return False - def readable(self): return GetNumberOfConsoleInputEvents(self.handle, ctypes.byref(DWORD(0))) != 0 - def writable(self): n = DWORD(0); return WriteConsoleW(self.handle, ctypes.c_void_p(), n, ctypes.byref(n), ctypes.c_void_p()) != 0 - def readwcharsinto(self, buf, n): - nr = DWORD(n) - old_error = ctypes.get_last_error() - ctypes.set_last_error(0) - success = ReadConsoleW(self.handle, buf, nr, ctypes.byref(nr), ctypes.c_void_p()) - error = ctypes.get_last_error() - ctypes.set_last_error(old_error) - if not success: raise ctypes.WinError(error) - ERROR_OPERATION_ABORTED = 995 - if nr.value == 0 and error == ERROR_OPERATION_ABORTED: - # Apparently this can trigger pending KeyboardInterrupts? - time.sleep(1.0 / (1 << 64)) - raise KeyboardInterrupt() # If Python doesn't raise it, we can - return nr.value - def writewchars(self, buf, n): - nw = DWORD(n) - if not WriteConsoleW(self.handle, buf, nw, ctypes.byref(nw), ctypes.c_void_p()): - raise ctypes.WinError() - return nw.value - -class WindowsConsoleRawIO(WindowsConsoleIOMixin, io.RawIOBase): - def readinto(self, b): - wordsize = ctypes.sizeof(ctypes.c_wchar) - return self.readwcharsinto(getbuffer(b, True), len(b) // wordsize) * wordsize - def write(self, b): - wordsize = ctypes.sizeof(ctypes.c_wchar) - return self.writewchars(getbuffer(b, False), len(b) // wordsize) * wordsize - -class WindowsConsoleTextIO(WindowsConsoleIOMixin, io.TextIOBase): - buf = None - buffered = unicode() - translate = True - def getbuf(self, ncodeunits): - buf = self.buf - if buf is None or len(buf) < ncodeunits: - self.buf = buf = ctypes.create_unicode_buffer(ncodeunits) - return buf - @staticmethod # Don't let classes override this... they can override the caller instead - def do_read(self, nchars, translate_newlines): - prenewline = os.linesep[:-1] - newline = os.linesep[-1:] - empty = os.linesep[:0] - if nchars is None or nchars < -1: nchars = -1 - ncodeunits = nchars if nchars >= 0 else io.DEFAULT_BUFFER_SIZE # Unit mismatch, but doesn't matter; we'll loop - buf = None - istart = 0 - while True: - iend = self.buffered.find(newline, istart, min(istart + nchars, len(self.buffered)) if nchars >= 0 else None) if newline is not None else nchars - if iend >= 0: iend += len(newline) if newline is not None else 0 - if 0 <= iend <= len(self.buffered): - break - if buf is None: buf = self.getbuf(ncodeunits) - istart = len(self.buffered) - chunk = buf[:self.readwcharsinto(buf, ncodeunits)] - if translate_newlines: chunk = chunk.replace(prenewline, empty) - if chunk.startswith('\x1A'): # EOF on Windows (Ctrl+Z) at the beginning of a line results in the entire rest of the buffer being discarded - iend = istart - break - # Python 2 and Python 3 behaviors differ on Windows... Python 2's sys.stdin.readline() just deletes the next character if it sees EOF in the middle of a string! I won't emulate that here. - self.buffered += chunk # We're relying on Python's concatenation optimization here... we don't do it ourselves, since we want self.buffered to be valid every iteration in case there is an exception raised - result = self.buffered[:iend] - self.buffered = self.buffered[iend:] - return result - def read(self, nchars=-1): return WindowsConsoleTextIO.do_read(self, nchars, None, self.translate) - def readline(self, nchars=-1): return WindowsConsoleTextIO.do_read(self, nchars, self.translate) - def write(self, text): buf = ctypes.create_unicode_buffer(text); return self.writewchars(buf, max(len(buf) - 1, 0)) - -def wrap_windows_console_io(stream, is_output): - fd = None - if stream is not None and sys.version_info[0] < 3 and msvcrt and (is_output or pythonapi) and isatty(stream): - try: fd = stream.fileno() - except io.UnsupportedOperation: pass - result = stream - if fd is not None: - f = GetConsoleOutputCP if is_output else GetConsoleCP - if not f or f() != CP_UTF8: - try: - if True or is_output: - result = WindowsConsoleTextIO(fd) - else: - result = io.TextIOWrapper((io.BufferedWriter if is_output else io.BufferedReader)(WindowsConsoleRawIO(fd)), 'utf-16-le', 'strict', line_buffering=True) - except IOError: pass - return result - -class NonOwningTextIOWrapper(io.TextIOWrapper): - def __init__(self, base_textiowrapper, **kwargs): - assert isinstance(base_textiowrapper, io.TextIOWrapper) - self.base = base_textiowrapper # must keep a reference to this alive so it doesn't get closed - super(NonOwningTextIOWrapper, self).__init__(base_textiowrapper.buffer, **kwargs) - def close(self): - super(NonOwningTextIOWrapper, self).flush() - -def wrap_unicode_stdio(stream, is_writer, encoding): # The underlying stream must NOT be used directly until the stream returned by this function is disposed of - if isinstance(stream, io.TextIOWrapper): - stream.flush() # Make sure nothing is left in the buffer before we re-wrap it - none = object() - kwargs = {} - for key in ['encoding', 'errors', 'newline', 'line_buffering', 'write_through']: - value = getattr(stream, 'newlines' if key == 'newline' else key, none) - if value is not none: - kwargs[key] = value - kwargs['encoding'] = encoding - result = NonOwningTextIOWrapper(stream, **kwargs) - elif 'PYTHONIOENCODING' not in os.environ and str == bytes and stream in (sys.stdin, sys.stdout, sys.stderr): - result = (codecs.getwriter if is_writer else codecs.getreader)(encoding)(stream) - else: - result = stream - return result - -class StringEscapeParser(object): - def __init__(self): - import re - self.pattern = re.compile("\"((?:[^\"\\n]+|\\\\.)*)(?:\"|$)|\'([^\'\\n]*)(?:\'|$)|(\\S+)") - self.escape_pattern = re.compile("\\\\(.)", re.DOTALL) - @staticmethod - def escape_replacement(m): - text = m.group(1) - if text == "\\": text = "\\" - elif text == "/": text = "\n" - elif text == "n": text = "\n" - elif text == "r": text = "\r" - elif text == "t": text = "\t" - elif text == "v": text = "\v" - elif text == "f": text = "\f" - elif text == "a": text = "\a" - elif text == "b": text = "\b" - return text - def __call__(self, s): - escape_pattern = self.escape_pattern - escape_replacement = self.escape_replacement - result = [] - for match in self.pattern.finditer(s): - [m1, m2, m3] = match.groups() - if m1 is not None: result.append(escape_pattern.sub(escape_replacement, m1)) - if m2 is not None: result.append(m2) - if m3 is not None: result.append(escape_pattern.sub(escape_replacement, m3)) - return result - -class Database(object): - def __init__(self, name, *args, **kwargs): - self.connection = sqlite3.connect(name, *args, **kwargs) - self.cursor = self.connection.cursor() - self.name = name # assign name only AFTER cursor is created - -def isatty(file_or_fd): - result = True - method = getattr(file_or_fd, 'isatty', None) if not isinstance(file_or_fd, int) else None # this check is just an optimization - if method is not None: - try: tty = method() - except io.UnsupportedOperation: tty = None - result = result and tty is not None and tty - method = getattr(file_or_fd, 'fileno', None) if not isinstance(file_or_fd, int) else None # this check is just an optimization - if method is not None: - try: fd = method() - except io.UnsupportedOperation: fd = None - result = result and fd is not None and os.isatty(fd) and (not msvcrt or GetConsoleMode(msvcrt.get_osfhandle(fd), ctypes.byref(DWORD(0))) != 0) - return result - -def can_call_input_for_stdio(stream): - return stream == sys.stdin and sys.version_info[0] >= 3 - -class StdIOProxy(object): - # Maybe useful later: codecs.StreamRecoder(bytesIO, codec.decode, codec.encode, codec.streamwriter, codec.streamreader, errors='surrogateescape') - def __init__(self, stdin, stdout, stderr, codec, allow_set_code_page): - self.codec = codec - streams = (stdin, stdout, stderr) - for stream in streams: - assert isinstance(stream, io.IOBase) or sys.version_info[0] < 3 and isinstance(stream, file) or hasattr(stream, 'mode'), "unable to determine stream type" - assert not isinstance(stream, io.RawIOBase), "RAW I/O APIs are different and not supported" - self.streaminfos = tuple(map(lambda stream: - ( - stream, - isinstance(stream, io.BufferedIOBase) or isinstance(stream, io.RawIOBase) or not isinstance(stream, io.TextIOBase) and 'b' in stream.mode, - isinstance(stream, io.TextIOBase) or not (isinstance(stream, io.BufferedIOBase) or isinstance(stream, io.RawIOBase)) and 'b' not in stream.mode, - allow_set_code_page - ), - streams)) - @property - def stdin(self): return self.streaminfos[0][0] - @property - def stdout(self): return self.streaminfos[1][0] - @property - def stderr(self): return self.streaminfos[2][0] - def _coerce(self, streaminfo, codec, arg): - stream = streaminfo[0] - can_binary = streaminfo[1] - can_text = streaminfo[2] - if not isinstance(arg, bytes) and not isinstance(arg, buffer) and not isinstance(arg, unicode): - arg = unicode(arg) - if isinstance(arg, bytes) or isinstance(arg, buffer): - if not can_binary: - arg = codec.decode(arg, 'surrogateescape')[0] - elif isinstance(arg, unicode): - if not can_text: - arg = codec.encode(unicode(arg), 'strict')[0] - return arg - @staticmethod - def _do_readline(stream, allow_set_code_page, *args): - new_code_page = CP_UTF8 - old_code_page = GetConsoleCP() if msvcrt and GetConsoleCP and isatty(stream) else None - if old_code_page == new_code_page: old_code_page = None # Don't change code page if it's already correct... - if old_code_page is not None: - if not SetConsoleCP(new_code_page): - old_code_page = None - try: - result = stream.readline(*args) - finally: - if old_code_page is not None: - SetConsoleCP(old_code_page) - return result - @staticmethod - def _do_write(stream, allow_set_code_page, *args): - new_code_page = CP_UTF8 - old_code_page = GetConsoleOutputCP() if msvcrt and GetConsoleOutputCP and isatty(stream) else None - if old_code_page == new_code_page: old_code_page = None # Don't change code page if it's already correct... - if old_code_page is not None: - if not SetConsoleOutputCP(new_code_page): - old_code_page = None - try: - result = stream.write(*args) - finally: - if old_code_page is not None: - SetConsoleCP(old_code_page) - return result - def _readln(self, streaminfo, codec, prompt): - stream = streaminfo[0] - can_binary = streaminfo[1] - allow_set_code_page = streaminfo[3] - if can_call_input_for_stdio(stream) and not can_binary: # input() can't work with binary data - result = self._coerce(streaminfo, codec, "") - try: - result = input(*((self._coerce(streaminfo, codec, prompt),) if prompt is not None else ())) - result += self._coerce(streaminfo, codec, "\n") - except EOFError: pass - else: - self.output(*((prompt,) if prompt is not None else ())) - self.error() - result = StdIOProxy._do_readline(stream, allow_set_code_page) - return result - def _writeln(self, streaminfo, codec, *args, **kwargs): - stream = streaminfo[0] - allow_set_code_page = streaminfo[3] - flush = kwargs.pop('flush', True) - kwargs.setdefault('end', '\n') - kwargs.setdefault('sep', ' ') - end = kwargs.get('end') - sep = kwargs.get('sep') - first = True - for arg in args: - if first: first = False - elif sep is not None: - StdIOProxy._do_write(stream, allow_set_code_page, self._coerce(streaminfo, codec, sep)) - StdIOProxy._do_write(stream, allow_set_code_page, self._coerce(streaminfo, codec, arg)) - if end is not None: - StdIOProxy._do_write(stream, allow_set_code_page, self._coerce(streaminfo, codec, end)) - if flush: stream.flush() - def inputln(self, prompt=None): return self._readln(self.streaminfos[0], self.codec, prompt) - def output(self, *args, **kwargs): kwargs.setdefault('end', None); return self._writeln(self.streaminfos[1], self.codec, *args, **kwargs) - def outputln(self, *args, **kwargs): return self._writeln(self.streaminfos[1], self.codec, *args, **kwargs) - def error(self, *args, **kwargs): kwargs.setdefault('end', None); return self._writeln(self.streaminfos[2], self.codec, *args, **kwargs) - def errorln(self, *args, **kwargs): return self._writeln(self.streaminfos[2], self.codec, *args, **kwargs) - -class bytes_comparable_with_unicode(bytes): # For Python 2/3 compatibility, to allow implicit conversion between strings and bytes when it is safe. (Used for strings like literals which we know be safe.) - codec = codecs.lookup('ascii') # MUST be a safe encoding - @classmethod - def coerce(cls, other, for_output=False): - return cls.codec.encode(other)[0] if not isinstance(other, bytes) else bytes_comparable_with_unicode(other) if for_output else other - @classmethod - def translate_if_bytes(cls, value): - if value is not None and isinstance(value, bytes): value = cls(value) - return value - def __hash__(self): return super(bytes_comparable_with_unicode, self).__hash__() # To avoid warning - def __eq__(self, other): return super(bytes_comparable_with_unicode, self).__eq__(self.coerce(other)) - def __ne__(self, other): return super(bytes_comparable_with_unicode, self).__ne__(self.coerce(other)) - def __lt__(self, other): return super(bytes_comparable_with_unicode, self).__lt__(self.coerce(other)) - def __gt__(self, other): return super(bytes_comparable_with_unicode, self).__gt__(self.coerce(other)) - def __le__(self, other): return super(bytes_comparable_with_unicode, self).__le__(self.coerce(other)) - def __ge__(self, other): return super(bytes_comparable_with_unicode, self).__ge__(self.coerce(other)) - def __getitem__(self, index): return self.coerce(super(bytes_comparable_with_unicode, self).__getitem__(index), True) - def __add__(self, other): return self.coerce(super(bytes_comparable_with_unicode, self).__add__(self.coerce(other)), True) - def __iadd__(self, other): return self.coerce(super(bytes_comparable_with_unicode, self).__iadd__(self.coerce(other)), True) - def __radd__(self, other): return self.coerce(self.coerce(other).__add__(self), True) - def find(self, other, *args): return super(bytes_comparable_with_unicode, self).find(self.coerce(other), *args) - def join(self, others): return self.coerce(super(bytes_comparable_with_unicode, self).join(map(self.coerce, others)), True) - def startswith(self, other): return super(bytes_comparable_with_unicode, self).startswith(self.coerce(other)) - def __str__(self): return self.codec.decode(self)[0] - if str == bytes: - __unicode__ = __str__ - def __str__(self): raise NotImplementedError() - -def wrap_bytes_comparable_with_unicode_readline(readline): - def callback(*args): - line = readline(*args) - line = bytes_comparable_with_unicode.translate_if_bytes(line) - return line - return callback - -def main(program, *args, **kwargs): # **kwargs = dict(stdin=file, stdout=file, stderr=file); useful for callers who import this module - import argparse # slow import (compiles regexes etc.), so don't import it until needed - argparser = argparse.ArgumentParser( - prog=os.path.basename(program), - usage=None, - description=None, - epilog=None, - parents=[], - formatter_class=argparse.RawTextHelpFormatter) - argparser.add_argument('-version', '--version', action='store_true', help="show SQLite version") - argparser.add_argument('-batch', '--batch', action='store_true', help="force batch I/O") - argparser.add_argument('-init', '--init', metavar="FILE", help="read/process named file") - argparser.add_argument('filename', nargs='?', metavar="FILENAME", help="is the name of an SQLite database.\nA new database is created if the file does not previously exist.") - argparser.add_argument('sql', nargs='*', metavar="SQL", help="SQL commnds to execute after opening database") - argparser.add_argument('--readline', action='store', metavar="(true|false)", default="true", choices=("true", "false"), help="whether to import readline if available (default: %(default)s)") - argparser.add_argument('--self-test', action='store_true', help="perform a basic self-test") - argparser.add_argument('--cross-test', action='store_true', help="perform a basic test against the official executable") - argparser.add_argument('--unicode-stdio', action='store', metavar="(true|false)", default="true", choices=("true", "false"), help="whether to enable Unicode wrapper for standard I/O (default: %(default)s)") - argparser.add_argument('--console', action='store', metavar="(true|false)", default="true", choices=("true", "false"), help="whether to auto-detect and use console window APIs (default: %(default)s)") - argparser.add_argument('--encoding', default=ENCODING, help="the default encoding to use (default: %(default)s)") - (stdin, stdout, stderr) = (kwargs.pop('stdin', sys.stdin), kwargs.pop('stdout', sys.stdout), kwargs.pop('stderr', sys.stderr)) - parsed_args = argparser.parse_args(args) - codec = codecs.lookup(parsed_args.encoding or argparser.get_default('encoding')) - if parsed_args.self_test: self_test(codec) - if parsed_args.cross_test: cross_test("sqlite3", codec) - parse_escaped_strings = StringEscapeParser() - if parsed_args.unicode_stdio == "true": - stdin = wrap_unicode_stdio(stdin, False, codec.name) - stdout = wrap_unicode_stdio(stdout, True, codec.name) - stderr = wrap_unicode_stdio(stderr, True, codec.name) - if parsed_args.console == "true": - stdin = wrap_windows_console_io(stdin, False) - stdout = wrap_windows_console_io(stdout, True) - stderr = wrap_windows_console_io(stderr, True) - allow_set_code_page = sys.version_info[0] < 3 and False # This is only necessary on Python 2 if we use the default I/O functions instead of bypassing to ReadConsole()/WriteConsole() - stdio = StdIOProxy(stdin, stdout, stderr, codec, allow_set_code_page) - db = None - no_args = len(args) == 0 - init_sql = parsed_args.sql - is_nonpipe_input = stdin.isatty() # NOT the same thing as TTY! (NUL and /dev/null are the difference) - init_show_prompt = not parsed_args.batch and is_nonpipe_input - if not parsed_args.batch and isatty(stdin) and (parsed_args.readline == "true" or __name__ == '__main__') and parsed_args.readline != "false": - try: - with warnings.catch_warnings(): - warnings.filterwarnings('ignore', category=DeprecationWarning) - import readline - except ImportError: pass - if parsed_args and parsed_args.version: - stdio.outputln(sqlite3.sqlite_version); - else: - filename = parsed_args.filename - if filename is None: filename = ":memory:" - db = Database(filename, isolation_level=None) - def exec_script(db, filename, ignore_io_errors): - try: - with io.open(filename, 'r', encoding=codec.name) as f: # Assume .sql files are text -- any binary data inside them should be X'' encoded, not embedded directly - for command in sql_commands(wrap_bytes_comparable_with_unicode_readline(lambda *args: (lambda s: (s) or None)(f.readline()))): - result = exec_command(db, command, False and ignore_io_errors) - if result is not None: - return result - except IOError as ex: - stdio.errorln(ex) - if not ignore_io_errors: return ex.errno - def raise_invalid_command_error(command): - if isinstance(command, bytes): command = codec.decode(command)[0] - if command.startswith("."): command = command[1:] - raise RuntimeError("Error: unknown command or invalid arguments: \"%s\". Enter \".help\" for help" % (command.rstrip().replace("\\", "\\\\").replace("\"", "\\\""),)) - def exec_command(db, command, ignore_io_errors): - results = None - query = None - query_parameters = {} - try: - if command.startswith("."): - args = list(parse_escaped_strings(command)) - if args[0] in (".quit", ".exit"): - return 0 - elif args[0] == ".help": - stdio.error(""" -.cd DIRECTORY Change the working directory to DIRECTORY -.dump Dump the database in an SQL text format -.exit Exit this program -.help Show this message -.open FILE Close existing database and reopen FILE -.print STRING... Print literal STRING -.quit Exit this program -.read FILENAME Execute SQL in FILENAME -.schema ?PATTERN? Show the CREATE statements matching PATTERN -.show Show the current values for various settings -.tables ?TABLE? List names of tables -""".lstrip()) - elif args[0] == ".cd": - if len(args) != 2: raise_invalid_command_error(command) - os.chdir(args[1]) - elif args[0] == ".dump": - if len(args) != 1: raise_invalid_command_error(command) - foreign_keys = db.cursor.execute("PRAGMA foreign_keys;").fetchone()[0] - if foreign_keys in (0, "0", "off", "OFF"): - stdio.outputln("PRAGMA foreign_keys=OFF;", flush=False) - for line in db.connection.iterdump(): - stdio.outputln(line, flush=False) - stdio.output() - elif args[0] == ".open": - if len(args) <= 1: raise_invalid_command_error(command) - filename = args[-1] - for option in args[+1:-1]: - raise ValueError("option %s not supported" % (repr(option),)) - try: db.__init__(filename) - except sqlite3.OperationalError as ex: - ex.args = ex.args[:0] + ("Error: unable to open database \"%s\": %s" % (filename, ex.args[0]),) + ex.args[1:] - raise - elif args[0] == ".print": - stdio.outputln(*args[1:]) - elif args[0] == ".read": - if len(args) != 2: raise_invalid_command_error(command) - exec_script(db, args[1], ignore_io_errors) - elif args[0] == ".schema": - if len(args) > 2: raise_invalid_command_error(command) - pattern = args[1] if len(args) > 1 else None - query_parameters['type'] = 'table' - if pattern is not None: - query_parameters['pattern'] = pattern - query = "SELECT sql || ';' FROM sqlite_master WHERE type = :type" + (" AND name LIKE :pattern" if pattern is not None else "") + ";" - elif args[0] == ".show": - if len(args) > 2: raise_invalid_command_error(command) - stdio.errorln(" filename:", db.name) - elif args[0] == ".tables": - if len(args) > 2: raise_invalid_command_error(command) - pattern = args[1] if len(args) > 1 else None - query_parameters['type'] = 'table' - if pattern is not None: - query_parameters['pattern'] = pattern - query = "SELECT name FROM sqlite_master WHERE type = :type" + (" AND name LIKE :pattern" if pattern is not None else "") + ";" - else: - raise_invalid_command_error(args[0]) - else: - query = command - if query is not None: - results = db.cursor.execute(query if isinstance(query, unicode) else codec.decode(query, 'surrogatereplace')[0], query_parameters) - except (RuntimeError, OSError, FileNotFoundError, sqlite3.OperationalError) as ex: - stdio.errorln(exception_encode(ex, codec)) - if results is not None: - for row in results: - stdio.outputln(*tuple(map(lambda item: item if item is not None else "", row)), sep="|", flush=False) - stdio.output() - if db: - if parsed_args and parsed_args.init: - if is_nonpipe_input: stdio.errorln("-- Loading resources from", parsed_args.init) - exec_script(db, parsed_args.init, False) - def read_stdin(index, not_in_the_middle_of_any_input, prev_line): - show_prompt = init_show_prompt - to_write = [] - if index < len(init_sql): - line = init_sql[index] - if not line.startswith(".") and not line.rstrip().endswith(";"): - line += ";" - elif index == len(init_sql) and len(init_sql) > 0: - line = None - else: - if show_prompt: - if not_in_the_middle_of_any_input: - show_prompt = False - if index == 0: - to_write.append("SQLite version %s (adapter version %s)\nEnter \".help\" for usage hints.\n" % (sqlite3.sqlite_version, sqlite3.version)) - if no_args: - to_write.append("Connected to a transient in-memory database.\nUse \".open FILENAME\" to reopen on a persistent database.\n") - if index > 0 and not prev_line: - to_write.append("\n") - to_write.append("%7s " % ("sqlite%s>" % ("",) if not_in_the_middle_of_any_input else "...>",)) - try: - line = stdio.inputln("".join(to_write)) - except KeyboardInterrupt: - line = "" - raise # just kidding, don't handle it for now... - return line - for command in sql_commands(wrap_bytes_comparable_with_unicode_readline(read_stdin)): - result = exec_command(db, command, True) - if result is not None: - return result - if init_show_prompt and len(init_sql) == 0: - stdio.outputln() - -def call_program(cmdline, input_text): - import subprocess - return subprocess.Popen(cmdline, bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=False).communicate(input_text) - -def test_query(): - hexcodec = codecs.lookup('hex_codec') - ascii = 'ascii' - data1 = b"\xD8\xA2" - data2 = b"\x01\x02\xFF\x01\xFF\xFE\xFD" - values = [data1, data2] - query_bytes = b'SELECT %s;' % (b", ".join(map(lambda b: b"X'%s'" % (hexcodec.encode(b)[0].upper(),), values)),) - expected_bytes = b"%s\n" % (b"|".join(values),) - return query_bytes, expected_bytes - -def cross_test(sqlite_cmdline, codec): - (query_bytes, expected_bytes) = test_query() - (official_output, official_error) = call_program(sqlite_cmdline, query_bytes) - # We can't use os.linesep here since binaries may belong to different platforms (Win32/MinGW vs. MSYS/Cygwin vs. WSL...) - official_output = official_output.replace(b"\r\n", b"\n") - official_error = official_error.replace(b"\r\n", b"\n") - if official_output != expected_bytes: - raise sqlite3.ProgrammingError("expected bytes are wrong: official %s != expected %s" % (repr(official_output), repr(expected_bytes))) - if official_error: - raise sqlite3.ProgrammingError("did not expect errors from official binary") - -def self_test(codec): - (query_bytes, expected_bytes) = test_query() - if not (lambda stdin, stdout, stderr: not main(sys.argv[0], stdin=stdin, stdout=stdout, stderr=stderr) and stdout.getvalue() == expected_bytes)(io.BytesIO(query_bytes), io.BytesIO(), io.BytesIO()): - raise sqlite3.ProgrammingError("byte I/O is broken") - if not (lambda stdin, stdout, stderr: not main(sys.argv[0], stdin=stdin, stdout=stdout, stderr=stderr) and stdout.getvalue() == codec.decode(expected_bytes, 'surrogateescape'))(io.StringIO(query_bytes.decode(ascii)), io.StringIO(), io.StringIO()): - raise sqlite3.ProgrammingError("string I/O is broken") - -if __name__ == '__main__': - import sys - exit_code = main(*sys.argv) - if exit_code not in (None, 0): raise SystemExit(exit_code) diff --git a/hw/sol-hw10/tests/by_parent_height.py b/hw/sol-hw10/tests/by_parent_height.py deleted file mode 100644 index 790bfe5c27..0000000000 --- a/hw/sol-hw10/tests/by_parent_height.py +++ /dev/null @@ -1,28 +0,0 @@ -test = { - 'name': 'by_parent_height', - 'points': 1, - 'suites': [ - { - 'type': 'sqlite', - 'ordered': True, - 'setup': """ - sqlite> .read hw10.sql - """, - 'cases': [ - { - 'locked': False, - 'code': r""" - sqlite> SELECT * FROM by_parent_height; - herbert - fillmore - abraham - delano - grover - barack - clinton - """, - }, - ], - }, - ] -} \ No newline at end of file diff --git a/hw/sol-hw10/tests/low_variance.py b/hw/sol-hw10/tests/low_variance.py deleted file mode 100644 index 022c0bf79f..0000000000 --- a/hw/sol-hw10/tests/low_variance.py +++ /dev/null @@ -1,21 +0,0 @@ -test = { - 'name': 'low_variance', - 'points': 1, - 'suites': [ - { - 'type': 'sqlite', - 'setup': """ - sqlite> .read hw10.sql - """, - 'cases': [ - { - 'locked': False, - 'code': r""" - sqlite> SELECT * FROM low_variance; - curly|1 - """, - }, - ], - }, - ] -} \ No newline at end of file diff --git a/hw/sol-hw10/tests/sentences.py b/hw/sol-hw10/tests/sentences.py deleted file mode 100644 index bca3cfd3d8..0000000000 --- a/hw/sol-hw10/tests/sentences.py +++ /dev/null @@ -1,22 +0,0 @@ -test = { - 'name': 'sentences', - 'points': 1, - 'suites': [ - { - 'type': 'sqlite', - 'setup': """ - sqlite> .read hw10.sql - """, - 'cases': [ - { - 'locked': False, - 'code': r""" - 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 - """, - }, - ], - }, - ] -} \ No newline at end of file diff --git a/hw/sol-hw10/tests/size_of_dogs.py b/hw/sol-hw10/tests/size_of_dogs.py deleted file mode 100644 index c18e680ccf..0000000000 --- a/hw/sol-hw10/tests/size_of_dogs.py +++ /dev/null @@ -1,25 +0,0 @@ -test = { - 'name': 'size_of_dogs', - 'points': 1, - 'suites': [ - { - 'type': 'sqlite', - 'setup': """ - sqlite> .read hw10.sql - """, - 'cases': [ - { - 'locked': False, - 'code': r""" - sqlite> SELECT name FROM size_of_dogs WHERE size="toy" OR size="mini"; - abraham - eisenhower - fillmore - grover - herbert - """, - }, - ], - }, - ] -} \ No newline at end of file diff --git a/hw/sol-hw11/hw11.ok b/hw/sol-hw11/hw11.ok deleted file mode 100644 index d89574e462..0000000000 --- a/hw/sol-hw11/hw11.ok +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "Homework 11", - "endpoint": "cal/cs61a/fa24/hw11", - "src": [ - "hw11.scm" - ], - "tests": { - "tests/*.py": "ok_test" - }, - "protocols": [ - "restore", - "file_contents", - "unlock", - "grading", - "analytics", - "backup" - ] -} \ No newline at end of file diff --git a/hw/sol-hw11/hw11.py b/hw/sol-hw11/hw11.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/hw/sol-hw11/hw11.zip b/hw/sol-hw11/hw11.zip deleted file mode 100644 index 66828ce1b7..0000000000 Binary files a/hw/sol-hw11/hw11.zip and /dev/null differ diff --git a/hw/sol-hw11/index.html b/hw/sol-hw11/index.html deleted file mode 100644 index 4973e474f8..0000000000 --- a/hw/sol-hw11/index.html +++ /dev/null @@ -1,273 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Homework 11 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Homework 11 Solutions - - -

-
- -

Solution Files

- - - -

This homework has nothing you need to submit to Gradescope. Rather, there are 3 surveys you need to fill out.

- - - -

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 deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/hw/sol-hw11/ok and /dev/null differ diff --git a/lab/sol-lab00/assets/gradescope-login.png b/lab/sol-lab00/assets/gradescope-login.png deleted file mode 100644 index 9bf2abf3dd..0000000000 Binary files a/lab/sol-lab00/assets/gradescope-login.png and /dev/null differ diff --git a/lab/sol-lab00/assets/gradescope-loginscreen.png b/lab/sol-lab00/assets/gradescope-loginscreen.png deleted file mode 100644 index f05ec00ac1..0000000000 Binary files a/lab/sol-lab00/assets/gradescope-loginscreen.png and /dev/null differ diff --git a/lab/sol-lab00/assets/gradescope-results.png b/lab/sol-lab00/assets/gradescope-results.png deleted file mode 100644 index f392afbbf4..0000000000 Binary files a/lab/sol-lab00/assets/gradescope-results.png and /dev/null differ diff --git a/lab/sol-lab00/assets/gradescope-submit.png b/lab/sol-lab00/assets/gradescope-submit.png deleted file mode 100644 index e24a75cf00..0000000000 Binary files a/lab/sol-lab00/assets/gradescope-submit.png and /dev/null differ diff --git a/lab/sol-lab00/assets/gradescope-upload.png b/lab/sol-lab00/assets/gradescope-upload.png deleted file mode 100644 index cf897a10a2..0000000000 Binary files a/lab/sol-lab00/assets/gradescope-upload.png and /dev/null differ diff --git a/lab/sol-lab00/index.html b/lab/sol-lab00/index.html deleted file mode 100644 index 1ab6bb6f32..0000000000 --- a/lab/sol-lab00/index.html +++ /dev/null @@ -1,534 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Lab 0 Solutions | Data C88C Fall 2024 - - - - - - -
- -
-
-
-

- -Lab 0 Solutions - - - - - - -

-
- -

Solution Files

- - - -

This lab is required for all students and counts toward your lab -score.

- - -

Introduction

- - -

This lab explains how to setup your computer to complete assignments and -introduces some of the basics of Python. If you need any help, post on Ed or -ask for help at your assigned lab section.

- -

Here's an outline of the lab:

- -
    -
  • Setup: Setting up the essential software for the course. This will require several - components, listed below.

    - -
      -
    • Install a Terminal: Install a terminal so you can interact with files - in this course and run OK commands. If you have a terminal on your computer - and feel comfortable using it, you can skip this part.
    • -
    • Install Python 3: Install the Python programming langauge to your - computer. If you already have Python 3.7 or later (ideally Python 3.9) installed, you can skip this part.
    • -
    • Install a Text Editor: Install software to edit .py files for this - course (e.g. VSCode, Atom, etc.). You can skip this part if you already - have a text editor you like.
    • -
  • -
  • Walkthrough: Using the Terminal: This walks you through how to use the - terminal and Python interpreter. If you already feel comfortable with both of these you do not need to read this section.
  • -
  • Walkthrough: Organizing your Files: This section walks you through how to - use your terminal to organize and navigate files for this course. Everyone - should at least skim this section, as it has important information specific - to this class, but if you are already comfortable navigating directory - structures with a terminal much of this will feel familar.
  • -
  • Required: Doing the Assignment: You must complete this section to get - points for the assignment. Here you will practice the different types of - problems you will be asked to do in lab, homework, and project assignments for - this course. The main goal of this assignment is to give you practice using our - software.
  • -
  • Required: Submitting the Assignment: You must complete this section to get - points for the assignment. This will walk you through how to turn in your work - after completing the previous section and how to verify that your work is - turned in on Gradescope.
  • -
  • Appendix: Useful Python Command Line Options: These are commands that are - useful in debugging your work, but not required to complete the lab. We - include them because we imagine they're likely to be helpful to you throughout - the course.
  • -
- - -

Setup

- - -

To setup your device, select the guide that corresponds to your operating system.

- - - - -

Your First Assignment

- - -

When working on assignments, ensure that your terminal's working directory is correct (which is likely where you unzipped the assignment).

- - -

1) What Would Python Do? (WWPD)

- - -

One component of lab assignments is to predict how the Python interpreter will -behave.

- -

Enter the following in your terminal to begin this section:

- -
python3 ok -q python-basics -u
- -

You will be prompted to enter the output of various statements/expressions. -You must enter them correctly to move on, but there is no penalty for -incorrect answers.

- -

The first time you run Ok, you will be prompted for your bCourses email. -Please follow these directions. -We use this information to associate your code with you when grading.

- -
>>> x = 20
->>> x + 2
-
______
22
->>> x -
______
20
->>> y = 5 ->>> y = y + 3 ->>> y * 2 -
______
16
->>> y + x -
______
28
- - - - -

2) Implementing Functions

- - -

Open the entire lab00 folder in VS Code. You can drag the folder onto the VS -Code application or open VS Code and use Open Folder... in the File menu. -Once you open the lab00 folder, you'll see the lab00.py file in the file -explorer on the left panel of your VS Code window. Click it to start editing -lab00.py, which is the file you will submit to receive credit for the lab.

- -

Important: Turn on Auto Save in the File menu of VS Code. Then, whenever -you change a file, the contents will be saved.

- -

Now complete the lab. You should see a function called twenty_twenty_four that -has a blank return statement. That blank is the only part you should change. -Replace it with an expression that evaluates to 2024. What's the most creative -expression you can come up with?

- - -

3) Running Tests

- - -

We will use a program called ok to test our code. ok will be -included in every assignment in this class.

- -

Switch to the terminal. Make sure you are in the lab00 directory we created -earlier. (The cd command lets you change directories.) Pro tip: If you -opened the lab00 folder in VS Code and select New Terminal in the Terminal -menu of VS Code, then the terminal will automatically be in the lab00 -directory.

- -

In that directory, you can type ls to verify that there are the following -files:

- -
    -
  • lab00.py: the starter file you just edited
  • -
  • ok: our testing program
  • -
  • lab00.ok: a configuration file for ok
  • -
- -

Now, let's test our code to make sure it works. You can run ok with this -command:

- -
python3 ok
- -
- -

Remember, if you are using Windows and the python3 command doesn't work, try using just -python or py.

- -
- -

If you wrote your code correctly and you finished unlocking your tests, you should see a successful test:

- -
=====================================================================
-Assignment: Lab 0
-=====================================================================
-
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Running tests
-
----------------------------------------------------------------------
-Test summary
-    2 test cases passed! No cases failed.
- -

If you didn't pass the tests, ok will instead show you something like this:

- -
---------------------------------------------------------------------
-Doctests for twenty_twenty_four
-
->>> from lab00 import *
->>> twenty_twenty_four()
-0
-
-# Error: expected
-#     2024
-# but got
-#     0
-
----------------------------------------------------------------------
-Test summary
-    0 test cases passed before encountering first failed test case
- -

Fix your code in your text editor until the test passes.

- -

Every time you run Ok, Ok will try to back up your work. Don't worry if it -says that the "Connection timed out" or that you're not enrolled in the -course. You can still submit to Gradescope and get credit.

- - -

Task C: Submitting the Assignment

- - -

Now that you have completed your first assignment, it's time to turn it in. You can follow these next steps to submit your work and get points.

- - -

Submit with Gradescope

- - -
    -
  1. Log in with School Credentials using your CalNet ID to Gradescope. You’ll be taken to your Dashboard as soon as you log in.

    - -

    - Gradescope login page - select Calnet ID -

  2. -
  3. On your Dashboard, select this course. You should have already been added to Gradescope. If this is not the case, please make a private Ed post. This will take you to the list of assignments in the course that you’re able to submit. On this list, you will see the status of the assignment, the release date, and the due date.
  4. -
  5. Click on the assignment Lab 0 to open it.
  6. -
  7. When the dialog box appears, click on the gray area that says Drag & Drop. This will open your file finder and you should select your code file lab00.py that you edited for this assignment.

    - -

    gradescope submit

  8. -
  9. Once you’ve chosen your file select the Upload button. When your upload is successful, you’ll see a confirmation message on your screen and you’ll receive an email.

    - -

    gradescope upload

  10. -
  11. Next, wait a few minutes for the autograder to grade your code file. Your final score will appear at the right and your output should be the same as the one you tested locally. You can check the code that you submitted at the top right where there is a tab labeled Code. If there are any errors, you can edit your lab00.py code and click Resubmit at the bottom of your screen to resubmit your code file. Assignments can be resubmitted as many times as you’d like before the deadline

    - -

    gradescope results

  12. -
- -

Your responses to WWPD questions are not submitted to Gradescope, and they don't need to be. Lab credit is based on code writing questions.

- -

Congratulations, you just submitted your first assignment!

- - -

Appendix: Useful Python Command Line Options

- -

Here are the most common ways to run Python on a file.

- -
    -
  1. Using no command-line options will run the code in the file you provide and - return you to the command line. If your file just contains function - definitions, you'll see no output unless there is a syntax error.

    - -
    python3 lab00.py
  2. -
  3. -i: The -i option runs the code in the file you provide, then opens - an interactive session (with a >>> prompt). You can then evaluate - expressions, for example calling functions you defined. To exit, type - exit(). You can also use the keyboard shortcut Ctrl-D on Linux/Mac - machines or Ctrl-Z Enter on Windows.

    - -

    If you edit the Python file while running it interactively, you will need to - exit and restart the interpreter in order for those changes to take effect.

    - -

    Here's how we can run lab00.py interactively:

    - -
    python3 -i lab00.py
  4. -
  5. -m doctest: Runs the doctests in a file, which are the examples in - the docstrings of functions.

    - -

    Each test in the file consists of >>> followed by some Python code and - the expected output.

    - -

    Here's how we can run the doctests in lab00.py:

    - -
     python3 -m doctest lab00.py
    - -

    When our code passes all of the doctests, no output is displayed. Otherwise, - information about the tests that failed will be displayed.

  6. -
- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/lab/sol-lab00/lab00.ok b/lab/sol-lab00/lab00.ok deleted file mode 100644 index 5611b63a54..0000000000 --- a/lab/sol-lab00/lab00.ok +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "Lab 0", - "endpoint": "cal/c88c/fa24/lab00", - "src": [ - "lab00.py" - ], - "tests": { - "lab*.py": "doctest", - "tests/*.py": "ok_test" - }, - "default_tests": [ - "twenty_twenty_four", - "python-basics" - ], - "protocols": [ - "restore", - "file_contents", - "analytics", - "unlock", - "grading", - "backup" - ] -} \ No newline at end of file diff --git a/lab/sol-lab00/lab00.py b/lab/sol-lab00/lab00.py deleted file mode 100644 index 094192bbad..0000000000 --- a/lab/sol-lab00/lab00.py +++ /dev/null @@ -1,9 +0,0 @@ -def twenty_twenty_four(): - """Come up with the most creative expression that evaluates to 2024 - using only numbers and the +, *, and - operators. - - >>> twenty_twenty_four() - 2024 - """ - return ((20 ** 2 + 22 ** 2) + ((2 ** 0 + 2 ** 2) * 20 * 22) + (20 + 2 * 2) * 20 * 2) // 2 + 2 - diff --git a/lab/sol-lab00/lab00.zip b/lab/sol-lab00/lab00.zip deleted file mode 100644 index 55d23da58c..0000000000 Binary files a/lab/sol-lab00/lab00.zip and /dev/null differ diff --git a/lab/sol-lab00/ok b/lab/sol-lab00/ok deleted file mode 100644 index 88874ff1a5..0000000000 Binary files a/lab/sol-lab00/ok and /dev/null differ diff --git a/lab/sol-lab00/tests/python-basics.py b/lab/sol-lab00/tests/python-basics.py deleted file mode 100644 index e1f91da641..0000000000 --- a/lab/sol-lab00/tests/python-basics.py +++ /dev/null @@ -1,26 +0,0 @@ -test = { - 'name': 'Python Basics', - 'points': 0, - 'suites': [ - { - 'type': 'wwpp', - 'cases': [ - { - 'code': """ - >>> x = 20 - >>> x + 2 - 22 - >>> x - 20 - >>> y = 5 - >>> y = y + 3 - >>> y * 2 - 16 - >>> y + x - 28 - """, - }, - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab01/index.html b/lab/sol-lab01/index.html deleted file mode 100644 index dfca134b46..0000000000 --- a/lab/sol-lab01/index.html +++ /dev/null @@ -1,605 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Lab 1 Solutions | Data C88C Fall 2024 - - - - - - -
- -
-
-
-

- -Lab 1 Solutions - - - - - - -

-
- -

Solution Files

- - - - - - -

Required Questions

- - - -

Review

- - - - -
-Here are the most common ways to run Python on a file. - -
    -
  1. Using no command-line options will run the code in the file you provide and - return you to the command line. If your file just contains function - definitions, you'll see no output unless there is a syntax error.

    - -
    python3 lab00.py
  2. -
  3. -i: The -i option runs the code in the file you provide, then opens - an interactive session (with a >>> prompt). You can then evaluate - expressions, for example calling functions you defined. To exit, type - exit(). You can also use the keyboard shortcut Ctrl-D on Linux/Mac - machines or Ctrl-Z Enter on Windows.

    - -

    If you edit the Python file while running it interactively, you will need to - exit and restart the interpreter in order for those changes to take effect.

    - -

    Here's how we can run lab00.py interactively:

    - -
    python3 -i lab00.py
  4. -
  5. -m doctest: Runs the doctests in a file, which are the examples in - the docstrings of functions.

    - -

    Each test in the file consists of >>> followed by some Python code and - the expected output.

    - -

    Here's how we can run the doctests in lab00.py:

    - -
     python3 -m doctest lab00.py
    - -

    When our code passes all of the doctests, no output is displayed. Otherwise, - information about the tests that failed will be displayed. -

-

- - - - -
-In 61A, we use a program called OK for autograding labs, homeworks, and -projects. - -

To use Ok to test a function, run the following command (replacing FUNCTION with the name of the function):

- -
python3 ok -q FUNCTION
- -

If your function contains a call to print that starts with "DEBUG:", then this line will be ignored by OK. (Otherwise, including extra print calls can cause tests to fail because of the additional output displayed.)

- -
print("DEBUG:", x)
- -

There are more features described on the Using OK page. -You can quickly generate most ok commands at ok-help. -

-

- - - -
-Here are examples of the division-related operators in Python 3: - -
- - - - - - - - - - - - -
True Division: /
(decimal division)
Floor Division: //
(integer division)
Modulo: %
(remainder)
-
>>> 1 / 5
-0.2
-
->>> 25 / 4
-6.25
-
->>> 4 / 2
-2.0
-
->>> 5 / 0
-ZeroDivisionError
-
-
-
>>> 1 // 5 # truncate result of true division
-0
-
->>> 25 // 4
-6
-
->>> 4 // 2
-2
-
->>> 5 // 0
-ZeroDivisionError
-
-
-
>>> 1 % 5
-1
-
->>> 25 % 4
-1
-
->>> 4 % 2
-0
-
->>> 5 % 0
-ZeroDivisionError
-
-
-
- -

A ZeroDivisionError occurs when dividing by 0.

- -

One useful technique involving the % operator is to check -whether a number x is divisible by another number y:

- -
x % y == 0
- -

For example, in order to check if x is an even number: x % 2 == 0 -

-

- - - -
-Most functions that you define will contain a return statement that provides -the value of the call expression used to call the function. - -

When Python executes a return statement, the function call terminates -immediately. If Python reaches the end of the function body without executing -a return statement, the function returns None.

- -

In contrast, the print function is used to display values. -Unlike a return statement, when Python evaluates a call to print, the -function does not terminate immediately.

- -
def what_prints():
-    print('Hello World!')
-    return 'Exiting this function.'
-    print('61A is awesome!')
-
->>> what_prints()
-Hello World!
-'Exiting this function.'
- -

Notice also that print will display text without the quotes, but -return will preserve the quotes. -

- - -

What Would Python Display? (WWPD)

- - - -

Q1: Return and Print

- - -

Use Ok to test your knowledge with the following "What Would Python Display?" questions:

python3 ok -q return-and-print -u
- -
- -
>>> def welcome():
-...     print('Go')
-...     return 'hello'
-...
->>> def cal():
-...     print('Bears')
-...     return 'world'
-...
->>> welcome()
-
______
Go 'hello'
->>> print(welcome(), cal()) -
______
Go Bears hello world
- - - - -

Write Code

- - - -

Q2: Debugging Quiz

- - -

The following is a quick quiz on different debugging techniques that will be -helpful for you to use in this class. You can refer to the -debugging article to answer the questions.

- -

Use Ok to test your understanding:

python3 ok -q debugging-quiz -u
- -
- - -

Q3: Pick a Digit

- - -

Implement digit, which takes positive integers n and k and has only a -single return statement as its body. It returns the digit of n that is k -positions to the left of the rightmost digit (the one's digit). If k is 0, -return the rightmost digit. If there is no digit of n that is k positions to -the left of the rightmost digit, return 0.

- -

Hint: Use // and % and the built-in pow function to isolate a -particular digit of n.

- - - -
def digit(n, k):
-    """Return the digit that is k from the right of n for positive integers n and k.
-
-    >>> digit(3579, 2)
-    5
-    >>> digit(3579, 0)
-    9
-    >>> digit(3579, 10)
-    0
-    """
-
return n // pow(10, k) % 10
- -
- -

Use Ok to test your code:

python3 ok -q digit
- -
- - -

Q4: Middle Number

- - -

Implement middle by writing a single return expression that evaluates to the -value that is neither the largest or smallest among three different integers -a, b, and c.

- -

Hint: Try combining all the numbers and then taking away the ones you don't -want to return.

- - - -
def middle(a, b, c):
-    """Return the number among a, b, and c that is not the smallest or largest.
-    Assume a, b, and c are all different numbers.
-
-    >>> middle(3, 5, 4)
-    4
-    >>> middle(30, 5, 4)
-    5
-    >>> middle(3, 5, 40)
-    5
-    >>> middle(3, 5, 40)
-    5
-    >>> middle(30, 5, 40)
-    30
-    """
-
return a + b + c - min(a, b, c) - max(a, b, c)
- -
- -

Use Ok to test your code:

python3 ok -q middle
- -
- - -

Syllabus Quiz

- - - -

Q5: Syllabus Quiz

- - -

Please fill out the Syllabus Quiz, -which confirms your understanding of the policies on the syllabus page (linked in the toolbar above).

- - -

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.

- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/lab/sol-lab01/lab01.ok b/lab/sol-lab01/lab01.ok deleted file mode 100644 index 9d2a4ffbd7..0000000000 --- a/lab/sol-lab01/lab01.ok +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "Lab 1", - "endpoint": "cal/c88c/fa24/lab01", - "src": [ - "lab01.py" - ], - "tests": { - "lab*.py": "doctest", - "tests/*.py": "ok_test" - }, - "default_tests": [ - "return-and-print", - "debugging-quiz", - "digit", - "middle" - ], - "protocols": [ - "restore", - "file_contents", - "analytics", - "unlock", - "grading", - "backup" - ] -} \ No newline at end of file diff --git a/lab/sol-lab01/lab01.py b/lab/sol-lab01/lab01.py deleted file mode 100644 index 2fbe3b8b71..0000000000 --- a/lab/sol-lab01/lab01.py +++ /dev/null @@ -1,30 +0,0 @@ -def digit(n, k): - """Return the digit that is k from the right of n for positive integers n and k. - - >>> digit(3579, 2) - 5 - >>> digit(3579, 0) - 9 - >>> digit(3579, 10) - 0 - """ - return n // pow(10, k) % 10 - - -def middle(a, b, c): - """Return the number among a, b, and c that is not the smallest or largest. - Assume a, b, and c are all different numbers. - - >>> middle(3, 5, 4) - 4 - >>> middle(30, 5, 4) - 5 - >>> middle(3, 5, 40) - 5 - >>> middle(3, 5, 40) - 5 - >>> middle(30, 5, 40) - 30 - """ - return a + b + c - min(a, b, c) - max(a, b, c) - diff --git a/lab/sol-lab01/lab01.zip b/lab/sol-lab01/lab01.zip deleted file mode 100644 index c8a5bf2102..0000000000 Binary files a/lab/sol-lab01/lab01.zip and /dev/null differ diff --git a/lab/sol-lab01/ok b/lab/sol-lab01/ok deleted file mode 100644 index 88874ff1a5..0000000000 Binary files a/lab/sol-lab01/ok and /dev/null differ diff --git a/lab/sol-lab01/tests/control.py b/lab/sol-lab01/tests/control.py deleted file mode 100644 index 431af4502f..0000000000 --- a/lab/sol-lab01/tests/control.py +++ /dev/null @@ -1,89 +0,0 @@ -test = { - 'name': 'Control', - 'points': 0, - 'suites': [ - { - 'type': 'wwpp', - 'cases': [ - { - 'code': """ - >>> def xk(c, d): - ... if c == 4: - ... return 6 - ... elif d >= 4: - ... return 6 + 7 + c - ... else: - ... return 25 - >>> xk(10, 10) - 23 - >>> xk(10, 6) - 23 - >>> xk(4, 6) - 6 - >>> xk(0, 0) - 25 - """, - }, - { - 'code': """ - >>> def how_big(x): - ... if x > 10: - ... print('huge') - ... elif x > 5: - ... return 'big' - ... if x > 0: - ... print('positive') - ... else: - ... print(0) - >>> how_big(7) # Be careful with quotation marks! - 'big' - >>> print(how_big(7)) # Be careful with quotation marks! - big - >>> how_big(12) - huge - positive - >>> print(how_big(12)) - huge - positive - None - >>> print(how_big(1), how_big(0)) - positive - 0 - None None - """, - } - ] - }, - { - 'type': 'wwpp', - 'cases': [ - { - 'code': """ - >>> n = 3 - >>> while n >= 0: # If this loops forever, just type Infinite Loop - ... n -= 1 - ... print(n) - 2 - 1 - 0 - -1 - """, - 'multiline': True, - }, - { - 'code': """ - >>> negative = -12 - >>> while negative: # If this loops forever, just type Infinite Loop - ... if negative + 6: - ... print(negative) - ... negative += 3 - -12 - -9 - -3 - """, - 'multiline': True, - }, - ] - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab01/tests/debugging-quiz.py b/lab/sol-lab01/tests/debugging-quiz.py deleted file mode 100644 index 7e79273aba..0000000000 --- a/lab/sol-lab01/tests/debugging-quiz.py +++ /dev/null @@ -1,157 +0,0 @@ -test = { - 'name': 'debugging-quiz', - 'points': 0, - 'suites': [ - { - 'type': 'concept', - 'cases': [ - { - 'question': """ - In the following traceback, what is the most recent function call? - Traceback (most recent call last): - File "temp.py", line 10, in - f("hi") - File "temp.py", line 2, in f - return g(x + x, x) - File "temp.py", line 5, in g - return h(x + y * 5) - File "temp.py", line 8, in h - return x + 0 - TypeError: must be str, not int - """, - 'choices': [ - 'f("hi")', - 'g(x + x, x)', - 'h(x + y * 5)', - ], - 'answer': "h(x + y * 5)", - 'hidden': False - }, - { - 'question': """ - In the following traceback, what is the cause of this error? - Traceback (most recent call last): - File "temp.py", line 10, in - f("hi") - File "temp.py", line 2, in f - return g(x + x, x) - File "temp.py", line 5, in g - return h(x + y * 5) - File "temp.py", line 8, in h - return x + 0 - TypeError: must be str, not int - """, - 'choices': [ - 'the code attempted to add a string to an integer', - 'the code looped infinitely', - 'there was a missing return statement', - ], - 'answer': "the code attempted to add a string to an integer", - 'hidden': False - }, - { - 'question': """ - How do you write a doctest asserting that square(2) == 4? - """, - 'choices': [ - """ - def square(x): - ''' - doctest: (2, 4) - ''' - return x * x - """, - """ - def square(x): - ''' - input: 2 - output: 4 - ''' - return x * x - """, - """ - def square(x): - ''' - square(2) - 4 - ''' - return x * x - """, - """ - def square(x): - ''' - >>> square(2) - 4 - ''' - return x * x - """ - ], - 'answer': """ - def square(x): - ''' - >>> square(2) - 4 - ''' - return x * x - """, - 'hidden': False - }, - { - 'question': "When should you use print statements?", - 'choices': [ - "For permanant debugging so you can have long term confidence in your code", - "To ensure that certain conditions are true at certain points in your code", - "To investigate the values of variables at certain points in your code" - ], - 'answer': "To investigate the values of variables at certain points in your code", - 'hidden': False - }, - { - 'question': "How do you prevent the ok autograder from interpreting print statements as output?", - 'choices': [ - "You don't need to do anything, ok only looks at returned values, not printed values", - "Print with 'DEBUG:' at the front of the outputted line", - "Print with # at the front of the outputted line", - ], - 'answer': "Print with 'DEBUG:' at the front of the outputted line", - 'hidden': False - }, - { - 'question': "What is the best way to open an interactive terminal to investigate a failing test for question sum_digits in assignment lab01?", - 'choices': [ - "python3 ok -q sum_digits -i", - "python3 ok -q sum_digits --trace", - "python3 ok -q sum_digits", - "python3 -i lab01.py" - ], - 'answer': "python3 ok -q sum_digits -i", - 'hidden': False - }, - # TODO: Figure out why --trace isn't working - # { - # 'question': "What is the best way to look at an environment diagram to investigate a failing test for question sum_digits in assignment lab01?", - # 'choices': [ - # "python3 ok -q sum_digits -i", - # "python3 ok -q sum_digits --trace", - # "python3 ok -q sum_digits", - # "python3 -i lab01.py" - # ], - # 'answer': "python3 ok -q sum_digits --trace", - # 'hidden': False - # }, - { - 'question': "Which of the following is NOT true?", - 'choices': [ - "Code that returns a wrong answer instead of crashing is generally better as it at least works fine", - "Testing is very important to ensure robust code", - "Debugging is not a substitute for testing", - "It is generally bad practice to release code with debugging print statements left in", - "It is generally good practice to release code with assertion statements left in", - ], - 'answer': "Code that returns a wrong answer instead of crashing is generally better as it at least works fine", - 'hidden': False - }, - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab01/tests/if-statements.py b/lab/sol-lab01/tests/if-statements.py deleted file mode 100644 index e8c2a1cebd..0000000000 --- a/lab/sol-lab01/tests/if-statements.py +++ /dev/null @@ -1,69 +0,0 @@ -test = { - 'name': 'What If?', - 'points': 0, - 'suites': [ - # { - # 'type': 'wwpp', - # 'cases': [ - # { - # 'code': """ - # >>> def so_big(x): - # ... if x > 10: - # ... print('huge') - # ... if x > 5: - # ... return 'big' - # ... if x > 0: - # ... print('small') - # ... print("nothin'") - # >>> so_big(7) - # 'big' - # >>> so_big(12) - # huge - # 'big' - # >>> so_big(1) - # small - # nothin' - # """, - # } - # ] - # }, - { - 'type': 'wwpp', - 'cases': [ - { - 'code': """ - >>> def ab(c, d): - ... if c > 5: - ... print(c) - ... elif c > 7: - ... print(d) - ... print('foo') - >>> ab(10, 20) - 10 - foo - """, - }, - { - 'code': """ - >>> def bake(cake, make): - ... if cake == 0: - ... cake = cake + 1 - ... print(cake) - ... if cake == 1: - ... print(make) - ... else: - ... return cake - ... return make - >>> bake(0, 29) - 1 - 29 - 29 - >>> bake(1, "mashed potatoes") - mashed potatoes - 'mashed potatoes' - """, - }, - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab01/tests/return-and-print.py b/lab/sol-lab01/tests/return-and-print.py deleted file mode 100644 index c0e101ed57..0000000000 --- a/lab/sol-lab01/tests/return-and-print.py +++ /dev/null @@ -1,28 +0,0 @@ -test = { - 'name': 'return-and-print', - 'points': 0, - 'suites': [ - { - 'type': 'wwpp', - 'cases': [ - { - 'code': """ - >>> def welcome(): - ... print('Go') - ... return 'hello' - >>> def cal(): - ... print('Bears') - ... return 'world' - >>> welcome() - Go - 'hello' - >>> print(welcome(), cal()) - Go - Bears - hello world - """, - }, - ] - }, - ] -} diff --git a/lab/sol-lab02/index.html b/lab/sol-lab02/index.html deleted file mode 100644 index ab9b6ccf82..0000000000 --- a/lab/sol-lab02/index.html +++ /dev/null @@ -1,1335 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Lab 2 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Lab 2 Solutions - - - - - - -

-
- -

Solution Files

- - - -

Topics

- - -

Consult this section if you need a refresher on the material for this lab. It's -okay to skip directly to the questions and refer back -here should you get stuck.

- - - -
- - -

Short Circuiting

- - -

What do you think will happen if we type the following into Python?

- -
1 / 0
- -

Try it out in Python! You should see a ZeroDivisionError. But what about this expression?

- -
True or 1 / 0
- -

It evaluates to True because Python's and and or operators short-circuit. That is, they don't necessarily evaluate every operand.

- -
- - - - - - - - - - - - - - - - - - - -
OperatorChecks if:Evaluates from left to right up to:Example
ANDAll values are trueThe first false valueFalse and 1 / 0 evaluates to False
ORAt least one value is trueThe first true valueTrue or 1 / 0 evaluates to True
-
- -

Short-circuiting happens when the operator reaches an operand that allows them to make a conclusion about the expression. For example, and will short-circuit as soon as it reaches the first false value because it then knows that not all the values are true.

- -

If and and or do not short-circuit, they just return the last -value; another way to remember this is that and and or always return the last -thing they evaluate, whether they short circuit or not. Keep in mind that and and or -don't always return booleans when using values other than True and False.

- -
- - - -
- - -

Higher-Order Functions

- - -

Variables are names bound to values, which can be primitives like 3 or -'Hello World', but they can also be functions. And since functions can take -arguments of any value, other functions can be passed in as arguments. This is -the basis for higher-order functions.

- -

A higher order function is a function that manipulates other functions by -taking in functions as arguments, returning a function, or both. We will -introduce the basics of higher order functions in this lab and will be -exploring many applications of higher order functions in our next lab.

- - -

Functions as arguments

- - -

In Python, function objects are values that can be passed around. We know that one -way to create functions is by using a def statement:

- -
def square(x):
-    return x * x
- -

The above statement created a function object with the intrinsic name square as -well as binded it to the name square in the current environment. Now let's try -passing it as an argument.

- -

First, let's write a function that takes in another function as an argument:

- -
def scale(f, x, k):
-    """ Returns the result of f(x) scaled by k. """
-    return k * f(x)
- -

We can now call scale on square and some other arguments:

- -
>>> scale(square, 3, 2) # Double square(3)
-18
->>> scale(square, 2, 5) # 5 times 2 squared
-20
- -

Note that in the body of the call to scale, the function object with the intrinsic -name square is bound to the parameter f. Then, we call square in the body of -scale by calling f(x).

- -

As we saw in the above section on lambda expressions, we can also pass -lambda expressions into call expressions!

- -
>>> scale(lambda x: x + 10, 5, 2)
-30
- -

In the frame for this call expression, the name f is bound to the function -created by the lambda expression lambda x: x + 10.

- - -

Functions that return functions

- - -

Because functions are values, they are valid as return values! Here's an example:

- -
def multiply_by(m):
-    def multiply(n):
-        return n * m
-    return multiply
- -

In this particular case, we defined the function multiply within the body of multiply_by -and then returned it. Let's see it in action:

- -
>>> multiply_by(3)
-<function multiply_by.<locals>.multiply at ...>
->>> multiply(4)
-Traceback (most recent call last):
-File "<stdin>", line 1, in <module>
-NameError: name 'multiply' is not defined
- -

A call to multiply_by returns a function, as expected. However, calling -multiply errors, even though that's the name we gave the inner function. This -is because the name multiply only exists within the frame where we evaluate -the body of multiply_by.

- -

So how do we actually use the inner function? Here are two ways:

- -
>>> times_three = multiply_by(3) # Assign the result of the call expression to a name
->>> times_three(5) # Call the inner function with its new name
-15
->>> multiply_by(3)(10) # Chain together two call expressions
-30
- -

The point is, because multiply_by returns a function, you can use its return -value just like you would use any other function.

- - - -
-
- - - -
- - -

Lambda Expressions

- - -

Lambda expressions are expressions that evaluate to functions by specifying two -things: the parameters and a return expression.

- -
lambda <parameters>: <return expression>
- -

While both lambda expressions and def statements create function objects, -there are some notable differences. lambda expressions work like other -expressions; much like a mathematical expression just evaluates to a number and -does not alter the current environment, a lambda expression -evaluates to a function without changing the current environment. Let's take a -closer look.

- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
lambdadef
TypeExpression that evaluates to a valueStatement that alters the environment
Result of executionCreates an anonymous lambda function with no intrinsic name.Creates a function with an intrinsic name and binds it to that - name in the current environment.
Effect on the environmentEvaluating a lambda expression does not create or - modify any variables.Executing a def statement both creates a new function - object and binds it to a name in the current environment.
UsageA lambda expression can be used anywhere that - expects an expression, such as in an assignment statement or as the - operator or operand to a call expression.After executing a def statement, the created - function is bound to a name. You should use this name to refer to the - function anywhere that expects an expression.
Example -
# A lambda expression by itself does not alter
-# the environment
-lambda x: x * x
-
-# We can assign lambda functions to a name
-# with an assignment statement
-square = lambda x: x * x
-square(3)
-
-# Lambda expressions can be used as an operator
-# or operand
-negate = lambda f, x: -f(x)
-negate(lambda x: x * x, 3)
def square(x):
-    return x * x
-
-# A function created by a def statement
-# can be referred to by its intrinsic name
-square(3)
-
- - -

YouTube link

- -
- - - -
- - -

Environment Diagrams

- - -

Environment diagrams are one of the best learning tools for understanding -lambda expressions and higher order functions because you're able to keep -track of all the different names, function objects, and arguments to functions. -We highly recommend drawing environment diagrams or using Python -tutor if you get stuck doing the WWPD problems below. -For examples of what environment diagrams should look like, try running some -code in Python tutor. Here are the rules:

- - -

Assignment Statements

- - -
    -
  1. Evaluate the expression on the right hand side of the = sign.
  2. -
  3. If the name found on the left hand side of the = doesn't already exist in - the current frame, write it in. If it does, erase the current binding. Bind the - value obtained in step 1 to this name.
  4. -
- -

If there is more than one name/expression in the statement, evaluate all the -expressions first from left to right before making any bindings.

- - - - -

def Statements

- - -
    -
  1. Draw the function object with its intrinsic name, formal parameters, and - parent frame. A function's parent frame is the frame in which the function was - defined.
  2. -
  3. If the intrinsic name of the function doesn't already exist in the current - frame, write it in. If it does, erase the current binding. Bind the newly - created function object to this name.
  4. -
- - - - -

Call expressions

- - -

Note: you do not have to go through this process for a built-in Python function like max or print.

- -
    -
  1. Evaluate the operator, whose value should be a function.
  2. -
  3. Evaluate the operands left to right.
  4. -
  5. Open a new frame. Label it with the sequential frame number, the intrinsic - name of the function, and its parent.
  6. -
  7. Bind the formal parameters of the function to the arguments whose values you - found in step 2.
  8. -
  9. Execute the body of the function in the new environment.
  10. -
- - - - -

Lambdas

- - -

Note: As we saw in the lambda expression section above, lambda -functions have no intrinsic name. When drawing lambda functions in -environment diagrams, they are labeled with the name lambda or with the -lowercase Greek letter λ. This can get confusing when there are -multiple lambda functions in an environment diagram, so you can distinguish -them by numbering them or by writing the line number on which they were defined.

- -
    -
  1. Draw the lambda function object and label it with λ, its formal - parameters, and its parent frame. A function's parent frame is the frame in - which the function was defined.
  2. -
- -

This is the only step. We are including this section to emphasize the fact that -the difference between lambda expressions and def statements is that -lambda expressions do not create any new bindings in the environment.

- - - - -

YouTube link

- -
- - -

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.

- - -

YouTube link

-
- - -

What Would Python Display?

- - -

Important: -For all WWPD questions, type Function if you believe the answer is -<function...>, Error if it errors, and Nothing if nothing is displayed.

- - -

Q1: WWPD: The Truth Will Prevail

- - -

Use Ok to test your knowledge with the following "What Would Python Display?" questions:

python3 ok -q short-circuit -u
- -
- -
>>> True and 13
-
______
13
->>> False or 0 -
______
0
->>> not 10 -
______
False
->>> not None -
______
True
- - - -
>>> True and 1 / 0
-
______
Error (ZeroDivisionError)
->>> True or 1 / 0 -
______
True
->>> -1 and 1 > 0 -
______
True
->>> -1 or 5 -
______
-1
->>> (1 + 1) and 1 -
______
1
->>> print(3) or "" -
______
3 ''
- - - -
>>> def f(x):
-...     if x == 0:
-...         return "zero"
-...     elif x > 0:
-...         return "positive"
-...     else:
-...         return ""
->>> 0 or f(1)
-
______
'positive'
->>> f(0) or f(-1) -
______
'zero'
->>> f(0) and f(-1) -
______
''
- - - - -

Q2: WWPD: Higher-Order Functions

- - -

Use Ok to test your knowledge with the following "What Would Python Display?" questions:

python3 ok -q hof-wwpd -u
- -
-
- -
>>> def cake():
-...    print('beets')
-...    def pie():
-...        print('sweets')
-...        return 'cake'
-...    return pie
->>> chocolate = cake()
-
______
beets
->>> chocolate -
______
Function
->>> chocolate() -
______
sweets 'cake'
->>> more_chocolate, more_cake = chocolate(), cake -
______
sweets
->>> more_chocolate -
______
'cake'
->>> def snake(x, y): -... if cake == more_cake: -... return chocolate -... else: -... return x + y ->>> snake(10, 20) -
______
Function
->>> snake(10, 20)() -
______
30
->>> cake = 'cake' ->>> snake(10, 20) -
______
30
- - - - -

Q3: WWPD: Lambda

- - -

Use Ok to test your knowledge with the following "What Would Python Display?" questions:

python3 ok -q lambda -u
- -
-
-As a reminder, the following two lines of code will not display any output in the -interactive Python interpreter when executed: - -
>>> x = None
->>> x
->>>
- -
>>> lambda x: x  # A lambda expression with one parameter x
-
______
<function <lambda> at ...>
->>> a = lambda x: x # Assigning the lambda function to the name a ->>> a(5) -
______
5
->>> (lambda: 3)() # Using a lambda expression as an operator in a call exp. -
______
3
->>> b = lambda x, y: lambda: x + y # Lambdas can return other lambdas! ->>> c = b(8, 4) ->>> c -
______
<function <lambda> at ...
->>> c() -
______
12
->>> d = lambda f: f(4) # They can have functions as arguments as well. ->>> def square(x): -... return x * x ->>> d(square) -
______
16
- - - - - - - -
>>> higher_order_lambda = lambda f: lambda x: f(x)
->>> g = lambda x: x * x
->>> higher_order_lambda(2)(g)  # Which argument belongs to which function call?
-
______
Error
->>> higher_order_lambda(g)(2) -
______
4
->>> call_thrice = lambda f: lambda x: f(f(f(x))) ->>> call_thrice(lambda y: y + 1)(0) -
______
3
->>> print_lambda = lambda z: print(z) # When is the return expression of a lambda expression executed? ->>> print_lambda -
______
Function
->>> one_thousand = print_lambda(1000) -
______
1000
->>> one_thousand # What did the call to print_lambda return? -
______
# print_lambda returned None, so nothing gets displayed
- - - - - - -

Coding Practice

- - - -

Q4: Composite Identity Function

- - -

Write a function that takes in two single-argument functions, f and g, and -returns another function that has a single parameter x. The returned -function should return True if f(g(x)) is equal to g(f(x)) and False -otherwise. You can assume the output of g(x) is a valid input for f and vice -versa.

- - - -
def composite_identity(f, g):
-    """
-    Return a function with one parameter x that returns True if f(g(x)) is
-    equal to g(f(x)). You can assume the result of g(x) is a valid input for f
-    and vice versa.
-
-    >>> add_one = lambda x: x + 1        # adds one to x
-    >>> square = lambda x: x**2          # squares x [returns x^2]
-    >>> b1 = composite_identity(square, add_one)
-    >>> b1(0)                            # (0 + 1) ** 2 == 0 ** 2 + 1
-    True
-    >>> b1(4)                            # (4 + 1) ** 2 != 4 ** 2 + 1
-    False
-    """
-
return lambda x: f(g(x)) == g(f(x)) - - # Alternate solution: - def h(x): - return f(g(x)) == g(f(x)) - return h
- -
- -
-We must create a function to take x using lambda or def, then compare the two quantities. -
- -

Use Ok to test your code:

python3 ok -q composite_identity
- -
- - -

Q5: Count Cond

- - -

Consider the following implementations of count_fives and count_primes which -use the sum_digits and is_prime functions from earlier assignments:

- -
def count_fives(n):
-    """Return the number of values i from 1 to n (including n)
-    where sum_digits(n * i) is 5.
-    >>> count_fives(10)  # Among 10, 20, 30, ..., 100, only 50 (10 * 5) has digit sum 5
-    1
-    >>> count_fives(50)  # 50 (50 * 1), 500 (50 * 10), 1400 (50 * 28), 2300 (50 * 46)
-    4
-    """
-    i = 1
-    count = 0
-    while i <= n:
-        if sum_digits(n * i) == 5:
-            count += 1
-        i += 1
-    return count
-
-def count_primes(n):
-    """Return the number of prime numbers up to and including n.
-    >>> count_primes(6)   # 2, 3, 5
-    3
-    >>> count_primes(13)  # 2, 3, 5, 7, 11, 13
-    6
-    """
-    i = 1
-    count = 0
-    while i <= n:
-        if is_prime(i):
-            count += 1
-        i += 1
-    return count
- - - -

The implementations look quite similar! Generalize this logic by writing a -function count_cond, which takes in a two-argument predicate function -condition(n, i). count_cond returns a one-argument function that takes -in n, which counts all the numbers from 1 to n that satisfy condition -when called.

- -

Note: -When we say condition is a predicate function, we mean that it is -a function that will return True or False.

- - - -
def sum_digits(y):
-    """Return the sum of the digits of non-negative integer y."""
-    total = 0
-    while y > 0:
-        total, y = total + y % 10, y // 10
-    return total
-
-def is_prime(n):
-    """Return whether positive integer n is prime."""
-    if n == 1:
-        return False
-    k = 2
-    while k < n:
-        if n % k == 0:
-            return False
-        k += 1
-    return True
-
-def count_cond(condition):
-    """Returns a function with one parameter N that counts all the numbers from
-    1 to N that satisfy the two-argument predicate function Condition, where
-    the first argument for Condition is N and the second argument is the
-    number from 1 to N.
-
-    >>> count_fives = count_cond(lambda n, i: sum_digits(n * i) == 5)
-    >>> count_fives(10)   # 50 (10 * 5)
-    1
-    >>> count_fives(50)   # 50 (50 * 1), 500 (50 * 10), 1400 (50 * 28), 2300 (50 * 46)
-    4
-
-    >>> is_i_prime = lambda n, i: is_prime(i) # need to pass 2-argument function into count_cond
-    >>> count_primes = count_cond(is_i_prime)
-    >>> count_primes(2)    # 2
-    1
-    >>> count_primes(3)    # 2, 3
-    2
-    >>> count_primes(4)    # 2, 3
-    2
-    >>> count_primes(5)    # 2, 3, 5
-    3
-    >>> count_primes(20)   # 2, 3, 5, 7, 11, 13, 17, 19
-    8
-    """
-
def counter(n): - i = 1 - count = 0 - while i <= n: - if condition(n, i): - count += 1 - i += 1 - return count - return counter
- -
- -
- -

One question that might be nice to ask is: -in what ways is the logic for count_fives and count_primes similar, -and in what ways are they different?

- -

The answer to the first question can tell us the logic that we want to -include in our count_cond function, while the answer to the second -question can tell us where in count_cond we want to be able to have -the difference in behavior observed between count_fives and -count_primes.

- -

It'll be helpful to also keep in mind that we want count_cond to return -a function that, when an argument n is passed in, will behave similarly -to count_fives or count_primes. In other words, count_cond is a -higher order function that returns a function, that then contains the -logic common to both count_fives and count_primes.

- -
- -

Use Ok to test your code:

python3 ok -q count_cond
- -
- - -

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.

- - -

Environment Diagram Practice

- - -

There is no Gradescope submission for this component.

- -

However, we still encourage you to do this problem on paper to develop -familiarity with Environment Diagrams, which might appear in an alternate -form on the exam. To check your work, you can try putting the code into PythonTutor.

- - -

Q6: HOF Diagram Practice

- - -

Draw the environment diagram that results from executing the code below on paper -or a whiteboard. Use tutor.cs61a.org to check your work.

- - - -
n = 7
-
-def f(x):
-    n = 8
-    return x + 1
-
-def g(x):
-    n = 9
-    def h():
-        return x + 1
-    return h
-
-def f(f, x):
-    return f(x + n)
-
-f = f(g, n)
-g = (lambda y: y())(f)
- -
- - -

Optional Questions

- - -

These questions are optional. If you don't complete them, you will -still receive credit for lab. They are great practice, so do them -anyway!

- - -

Q7: Multiple

- -

Write a function that takes in two numbers and returns the smallest number that -is a multiple of both. -

- -
def multiple(a, b):
-    """Return the smallest number n that is a multiple of both a and b.
-
-    >>> multiple(3, 4)
-    12
-    >>> multiple(14, 21)
-    42
-    """
-
n = 1 - while True: - if n % a == 0 and n % b == 0: - return n - n += 1
- - - -

Use Ok to test your code:

python3 ok -q multiple
- -
- - -

Q8: I Heard You Liked Functions...

- - -

Define a function cycle that takes in three functions f1, f2, and -f3, as arguments. cycle will return another function g that should -take in an integer argument n and return another function h. That -final function h should take in an argument x and cycle through -applying f1, f2, and f3 to x, depending on what n -was. Here's what the final function h should do to x for a few -values of n:

- -
    -
  • n = 0, return x
  • -
  • n = 1, apply f1 to x, or return f1(x)
  • -
  • n = 2, apply f1 to x and then f2 to the result of that, or - return f2(f1(x))
  • -
  • n = 3, apply f1 to x, f2 to the result of applying f1, - and then f3 to the result of applying f2, or f3(f2(f1(x)))
  • -
  • n = 4, start the cycle again applying f1, then f2, then f3, - then f1 again, or f1(f3(f2(f1(x))))
  • -
  • And so forth.
  • -
- -

Hint: most of the work goes inside the most nested function.

- - - -
def cycle(f1, f2, f3):
-    """Returns a function that is itself a higher-order function.
-
-    >>> def add1(x):
-    ...     return x + 1
-    >>> def times2(x):
-    ...     return x * 2
-    >>> def add3(x):
-    ...     return x + 3
-    >>> my_cycle = cycle(add1, times2, add3)
-    >>> identity = my_cycle(0)
-    >>> identity(5)
-    5
-    >>> add_one_then_double = my_cycle(2)
-    >>> add_one_then_double(1)
-    4
-    >>> do_all_functions = my_cycle(3)
-    >>> do_all_functions(2)
-    9
-    >>> do_more_than_a_cycle = my_cycle(4)
-    >>> do_more_than_a_cycle(2)
-    10
-    >>> do_two_cycles = my_cycle(6)
-    >>> do_two_cycles(1)
-    19
-    """
-
def g(n): - def h(x): - i = 0 - while i < n: - if i % 3 == 0: - x = f1(x) - elif i % 3 == 1: - x = f2(x) - else: - x = f3(x) - i += 1 - return x - return h - return g - - # Alternative recursive solution - def g(n): - def h(x): - if n == 0: - return x - return cycle(f2, f3, f1)(n - 1)(f1(x)) - return h - return g -
- -
- -
- -

There are three main pieces of information we need in order to calculate -the value that we want to return.

- -
    -
  1. The three functions that we will be cycling through, so f1, f2, f3.
  2. -
  3. The number of function applications we need, namely n. When n is 0, - we want our function to behave like the identity function (i.e. return the - input without applying any of our three functions to it).
  4. -
  5. The input that we start off with, namely x.
  6. -
- -

The functions are the parameters passed into cycle. We want the return -value of cycle to be a function ret_fn that takes in n and outputs -another function ret. ret is a function that takes in x and then will -cyclically apply the three passed in functions to the input until we have -reached n applications. Thus, most of the logic will go inside of ret.

- -

Solution:

- -

To figure out which function we should next use in our cycle, we can use the -mod operation via %, and loop through the function applications until we -have made exactly n function applications to our original input x.

- -
- -

Use Ok to test your code:

python3 ok -q cycle
- -
- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/lab/sol-lab02/lab02.ok b/lab/sol-lab02/lab02.ok deleted file mode 100644 index 246e10e8f0..0000000000 --- a/lab/sol-lab02/lab02.ok +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "Lab 2", - "endpoint": "cal/cs61a/fa24/lab02", - "src": [ - "lab02.py" - ], - "tests": { - "lab*.py": "doctest", - "tests/*.py": "ok_test" - }, - "default_tests": [ - "lambda", - "short-circuit", - "hof-wwpd", - "count_cond", - "composite_identity" - ], - "protocols": [ - "restore", - "file_contents", - "analytics", - "unlock", - "grading", - "backup" - ] -} \ No newline at end of file diff --git a/lab/sol-lab02/lab02.py b/lab/sol-lab02/lab02.py deleted file mode 100644 index 7ef103c6cf..0000000000 --- a/lab/sol-lab02/lab02.py +++ /dev/null @@ -1,144 +0,0 @@ - -def composite_identity(f, g): - """ - Return a function with one parameter x that returns True if f(g(x)) is - equal to g(f(x)). You can assume the result of g(x) is a valid input for f - and vice versa. - - >>> add_one = lambda x: x + 1 # adds one to x - >>> square = lambda x: x**2 # squares x [returns x^2] - >>> b1 = composite_identity(square, add_one) - >>> b1(0) # (0 + 1) ** 2 == 0 ** 2 + 1 - True - >>> b1(4) # (4 + 1) ** 2 != 4 ** 2 + 1 - False - """ - return lambda x: f(g(x)) == g(f(x)) - - # Alternate solution: - def h(x): - return f(g(x)) == g(f(x)) - return h - - -def sum_digits(y): - """Return the sum of the digits of non-negative integer y.""" - total = 0 - while y > 0: - total, y = total + y % 10, y // 10 - return total - -def is_prime(n): - """Return whether positive integer n is prime.""" - if n == 1: - return False - k = 2 - while k < n: - if n % k == 0: - return False - k += 1 - return True - -def count_cond(condition): - """Returns a function with one parameter N that counts all the numbers from - 1 to N that satisfy the two-argument predicate function Condition, where - the first argument for Condition is N and the second argument is the - number from 1 to N. - - >>> count_fives = count_cond(lambda n, i: sum_digits(n * i) == 5) - >>> count_fives(10) # 50 (10 * 5) - 1 - >>> count_fives(50) # 50 (50 * 1), 500 (50 * 10), 1400 (50 * 28), 2300 (50 * 46) - 4 - - >>> is_i_prime = lambda n, i: is_prime(i) # need to pass 2-argument function into count_cond - >>> count_primes = count_cond(is_i_prime) - >>> count_primes(2) # 2 - 1 - >>> count_primes(3) # 2, 3 - 2 - >>> count_primes(4) # 2, 3 - 2 - >>> count_primes(5) # 2, 3, 5 - 3 - >>> count_primes(20) # 2, 3, 5, 7, 11, 13, 17, 19 - 8 - """ - def counter(n): - i = 1 - count = 0 - while i <= n: - if condition(n, i): - count += 1 - i += 1 - return count - return counter - - -def multiple(a, b): - """Return the smallest number n that is a multiple of both a and b. - - >>> multiple(3, 4) - 12 - >>> multiple(14, 21) - 42 - """ - n = 1 - while True: - if n % a == 0 and n % b == 0: - return n - n += 1 - - - -def cycle(f1, f2, f3): - """Returns a function that is itself a higher-order function. - - >>> def add1(x): - ... return x + 1 - >>> def times2(x): - ... return x * 2 - >>> def add3(x): - ... return x + 3 - >>> my_cycle = cycle(add1, times2, add3) - >>> identity = my_cycle(0) - >>> identity(5) - 5 - >>> add_one_then_double = my_cycle(2) - >>> add_one_then_double(1) - 4 - >>> do_all_functions = my_cycle(3) - >>> do_all_functions(2) - 9 - >>> do_more_than_a_cycle = my_cycle(4) - >>> do_more_than_a_cycle(2) - 10 - >>> do_two_cycles = my_cycle(6) - >>> do_two_cycles(1) - 19 - """ - def g(n): - def h(x): - i = 0 - while i < n: - if i % 3 == 0: - x = f1(x) - elif i % 3 == 1: - x = f2(x) - else: - x = f3(x) - i += 1 - return x - return h - return g - - # Alternative recursive solution - def g(n): - def h(x): - if n == 0: - return x - return cycle(f2, f3, f1)(n - 1)(f1(x)) - return h - return g - - diff --git a/lab/sol-lab02/lab02.zip b/lab/sol-lab02/lab02.zip deleted file mode 100644 index 58f17c593c..0000000000 Binary files a/lab/sol-lab02/lab02.zip and /dev/null differ diff --git a/lab/sol-lab02/ok b/lab/sol-lab02/ok deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/lab/sol-lab02/ok and /dev/null differ diff --git a/lab/sol-lab02/tests/hof-wwpd.py b/lab/sol-lab02/tests/hof-wwpd.py deleted file mode 100644 index 705597eca6..0000000000 --- a/lab/sol-lab02/tests/hof-wwpd.py +++ /dev/null @@ -1,67 +0,0 @@ -test = { - 'name': 'Higher Order Functions', - 'points': 0, - 'suites': [ - { - 'type': 'wwpp', - 'cases': [ - # { - # 'code': """ - # >>> # If Python displays , type Function, if it errors type Error, if it displays nothing type Nothing - # >>> def even(f): - # ... def odd(x): - # ... if x < 0: - # ... return f(-x) - # ... return f(x) - # ... return odd - # >>> steven = lambda x: x - # >>> stewart = even(steven) - # >>> stewart - # Function - # >>> stewart(61) - # 61 - # >>> stewart(-4) - # 4 - # """, - # }, - { - 'code': """ - >>> # If Python displays , type Function, if it errors type Error, if it displays nothing type Nothing - >>> def cake(): - ... print('beets') - ... def pie(): - ... print('sweets') - ... return 'cake' - ... return pie - >>> chocolate = cake() - beets - >>> chocolate - Function - >>> chocolate() - sweets - 'cake' - >>> more_chocolate, more_cake = chocolate(), cake - sweets - >>> more_chocolate - 'cake' - >>> # Reminder: cake, more_cake, and chocolate were defined/assigned in the code above! - >>> # It might be helpful to refer to their definitions on the assignment website so you don't have to scroll as much! - >>> def snake(x, y): - ... if cake == more_cake: - ... return chocolate - ... else: - ... return x + y - >>> snake(10, 20) - Function - >>> snake(10, 20)() - sweets - 'cake' - >>> cake = 'cake' - >>> snake(10, 20) - 30 - """, - } - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab02/tests/lambda.py b/lab/sol-lab02/tests/lambda.py deleted file mode 100644 index 8e7b5cfe5c..0000000000 --- a/lab/sol-lab02/tests/lambda.py +++ /dev/null @@ -1,171 +0,0 @@ -test = { - 'name': 'Lambda the Free', - 'points': 0, - 'suites': [ - { - 'type': 'concept', - 'cases': [ - { - 'answer': 'A lambda expression does not automatically bind the function that it returns to a name.', - 'choices': [ - 'A lambda expression does not automatically bind the function that it returns to a name.', - 'A lambda expression cannot have more than two parameters.', - 'A lambda expression cannot return another function.', - 'A def statement can only have one line in its body.', - ], - 'question': r""" - Which of the following statements describes a difference between a def statement and a lambda expression? - """ - }, - { - 'answer': 'two', - 'choices': [ - 'one', - 'two', - 'three', - 'Not enough information', - ], - 'question': r""" - How many formal parameters does the following lambda expression have? - lambda a, b: c + d - """ - }, - { - 'answer': 'When the function returned by the lambda expression is called.', - 'choices': [ - 'When the function returned by the lambda expression is called.', - 'When you assign the lambda expression to a name.', - 'When the lambda expression is evaluated.', - 'When you pass the lambda expression into another function.', - ], - 'question': r""" - When is the return expression of a lambda expression executed? - """ - }, - ] - }, - { - 'type': 'wwpp', - 'cases': [ - { - 'code': """ - >>> # If Python displays , type Function, if it errors type Error, if it displays nothing type Nothing - >>> lambda x: x # A lambda expression with one parameter x - Function - >>> a = lambda x: x # Assigning a lambda function to the name a - >>> a(5) - 5 - >>> (lambda: 3)() # Using a lambda expression as an operator in a call exp. - 3 - >>> b = lambda x, y: lambda: x + y # Lambdas can return other lambdas! - >>> c = b(8, 4) - >>> c - Function - >>> c() - 12 - >>> d = lambda f: f(4) # They can have functions as arguments as well - >>> def square(x): - ... return x * x - >>> d(square) - 16 - """ - }, - # { - # 'code': """ - # >>> # - # >>> # Pay attention to the scope of variables - # >>> z = 3 - # >>> e = lambda x: lambda y: lambda: x + y + z - # >>> e(0)(1)() - # 4 - # >>> f = lambda z: x + z - # >>> f(3) - # Error - # """ - # }, - # { - # 'code': """ - # >>> x, y, z = 1, 2, 3 - # >>> a = lambda b: x + y + z - # >>> x += y - # >>> y -= z - # >>> a('lambdas are fun!') - # 5 - # """ - # }, - # { - # 'code': """ - # >>> x = None # remember to review the rules of WWPD given above! - # >>> x - # Nothing - # """ - # }, - { - 'code': """ - >>> # Try drawing an environment diagram if you get stuck! - >>> higher_order_lambda = lambda f: lambda x: f(x) - >>> g = lambda x: x * x - >>> higher_order_lambda(2)(g) # Which argument belongs to which function call? - Error - >>> higher_order_lambda(g)(2) - 4 - >>> call_thrice = lambda f: lambda x: f(f(f(x))) - >>> call_thrice(lambda y: y + 1)(0) - 3 - >>> print_lambda = lambda z: print(z) # When is the return expression of a lambda expression executed? - >>> print_lambda - Function - >>> one_thousand = print_lambda(1000) - 1000 - >>> one_thousand # What did the call to print_lambda return? If it displays nothing, write Nothing - Nothing - """ - }, - # { - # 'code':""" - # >>> # Try drawing an environment diagram if you get stuck! - # >>> bar = lambda y: lambda x: pow(x, y) - # >>> bar()(15) - # Error - # >>> foo = lambda: 32 - # >>> foobar = lambda x, y: x // y - # >>> a = lambda x: foobar(foo(), bar(4)(x)) - # >>> a(2) - # 2 - # >>> b = lambda x, y: print('summer') - # Nothing - # >>> c = b(4, 'dog') - # summer - # >>> print(c) - # None - # """ - # }, - ] - }, - # { - # 'type': 'wwpp', - # 'cases': [ - # # { - # # 'code': """ - # # >>> a = lambda b: b * 2 - # # Nothing - # # >>> a - # # Function - # # >>> a(a(a())) - # # Error - # # >>> a(a(a(2))) - # # 16 - # # >>> def d(): - # # ... print(None) - # # ... print('whoo') - # # >>> b = d() - # # None - # # whoo - # # >>> b - # # Nothing - # # """ - # # }, - # ] - # } - ] -} \ No newline at end of file diff --git a/lab/sol-lab02/tests/short-circuit.py b/lab/sol-lab02/tests/short-circuit.py deleted file mode 100644 index 34ad9eccb9..0000000000 --- a/lab/sol-lab02/tests/short-circuit.py +++ /dev/null @@ -1,72 +0,0 @@ -test = { - 'name': 'The Truth Will Prevail', - 'points': 0, - 'suites': [ - { - 'type': 'wwpp', - 'cases': [ - { - 'code': """ - >>> True and 13 - 13 - >>> False or 0 - 0 - >>> not 10 - False - >>> not None - True - """, - }, - ] - }, - { - 'type': 'wwpp', - 'cases': [ - { - 'code': """ - >>> True and 1 / 0 # If this errors, just type Error. - Error - >>> True or 1 / 0 # If this errors, just type Error. - True - >>> -1 and 1 > 0 # If this errors, just type Error. - True - >>> -1 or 5 - -1 - >>> (1 + 1) and 1 # If this errors, just type Error. If this is blank, just type Nothing. - 1 - """, - }, - { - 'code': """ - >>> print(3) or "" - 3 - '' - """, - 'multiline': True, - }, - ] - }, - { - 'type': 'wwpp', - 'cases': [ - { - 'code': """ - >>> def f(x): - ... if x == 0: - ... return "zero" - ... elif x > 0: - ... return "positive" - ... else: - ... return "" - >>> 0 or f(1) - 'positive' - >>> f(0) or f(-1) - 'zero' - >>> f(0) and f(-1) - '' - """, - }, - ] - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab03/construct_check.py b/lab/sol-lab03/construct_check.py deleted file mode 100644 index bee096e7e4..0000000000 --- a/lab/sol-lab03/construct_check.py +++ /dev/null @@ -1,180 +0,0 @@ -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/lab/sol-lab03/index.html b/lab/sol-lab03/index.html deleted file mode 100644 index 6b4b88e4dc..0000000000 --- a/lab/sol-lab03/index.html +++ /dev/null @@ -1,853 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Lab 3 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Lab 3 Solutions - - - - - - -

-
- -

Solution Files

- - - -

Topics

- - -

Consult this section if you need a refresher on the material for this lab. It's -okay to skip directly to the questions and refer back -here should you get stuck.

- - - -
- - -

Lists

- - -

A list is a data structure that can hold an ordered collection of items. -These items, known as elements, can be of any data type, including numbers, strings, or even other lists. -A comma-separated list of expressions in square brackets creates a list:

- -
>>> list_of_values = [2, 1, 3, True, 3]
->>> nested_list = [2, [1, 3], [True, [3]]]
- -

Each position in a list has an index, with the left-most element indexed 0.

- -
>>> list_of_values[0]
-2
->>> nested_list[1]
-[1, 3]
- -

A negative index counts from the end, with the right-most element indexed -1.

- -
>>> nested_list[-1]
-[True, [3]]
- -

Adding lists creates a longer list containing the elements of the added lists.

- -
>>> [1, 2] + [3] + [4, 5]
-[1, 2, 3, 4, 5]
- -
- - - -
- - -

List Comprehensions

- - -

A list comprehension describes the elements in a list and evaluates to a new list -containing those elements.

- -

There are two forms:

- -
[<expression> for <element> in <sequence>]
-[<expression> for <element> in <sequence> if <conditional>]
- -

Here's an example that starts with [1, 2, 3, 4], picks out the even elements -2 and 4 using if i % 2 == 0, then squares each of these using i*i. The -purpose of for i is to give a name to each element in [1, 2, 3, 4].

- -
>>> [i*i for i in [1, 2, 3, 4] if i % 2 == 0]
-[4, 16]
- -

This list comprehension evaluates to a list of:

- -
    -
  • The value of i*i
  • -
  • For each element i in the sequence [1, 2, 3, 4]
  • -
  • For which i % 2 == 0
  • -
- -

In other words, this list comprehension will create a new list that contains -the square of every even element of the original list [1, 2, 3, 4].

- -

We can also rewrite a list comprehension as an equivalent for statement, -such as for the example above:

- -
>>> result = []
->>> for i in [1, 2, 3, 4]:
-...     if i % 2 == 0:
-...         result = result + [i*i]
->>> result
-[4, 16]
- -
- - - -
- - -

For Statements

- - -

A for statement executes code for each element of a sequence, such as a list or range. Each time the code is executed, the name right after for is bound to a different element of the sequence.

- -
for <name> in <expression>:
-    <suite>
- -

First, <expression> is evaluated. It must evaluate to a sequence. Then, for each element in the sequence in order,

- -
    -
  1. <name> is bound to the element.
  2. -
  3. <suite> is executed.
  4. -
- -

Here is an example:

- -
for x in [-1, 4, 2, 0, 5]:
-    print("Current elem:", x)
- -

This would display the following:

- -
Current elem: -1
-Current elem: 4
-Current elem: 2
-Current elem: 0
-Current elem: 5
- -
- - - -
- - -

Ranges

- - -

A range is a data structure that holds integer sequences. A range can be created by:

- -
    -
  • range(stop) contains 0, 1, ..., stop - 1
  • -
  • range(start, stop) contains start, start + 1, ..., stop - 1
  • -
- -

Notice how the range function doesn't include the stop value; it generates numbers up to, but not including, the stop value.

- -

For example:

- -
>>> for i in range(3):
-...     print(i)
-...
-0
-1
-2
- -

While ranges and lists are both sequences, a range object is different from a list. A range can be converted to a list by calling list():

- -
>>> range(3, 6)
-range(3, 6)  # this is a range object
->>> list(range(3, 6))
-[3, 4, 5]  # list() converts the range object to a list
->>> list(range(5))
-[0, 1, 2, 3, 4]
->>> list(range(1, 6))
-[1, 2, 3, 4, 5]
- -
- -
- - -

Required Questions

- - -
- - - -

Lists

- - - -

Q1: WWPD: Lists & Ranges

- - -

Use Ok to test your knowledge with the following "What Would Python Display?" questions:

python3 ok -q lists-wwpd -u
- -
- -

Predict what Python will display when you type the following into the -interactive interpreter. Then try it to check your answers.

- -
>>> s = [7//3, 5, [4, 0, 1], 2]
->>> s[0]
-
______
2
->>> s[2] -
______
[4, 0, 1]
->>> s[-1] -
______
2
->>> len(s) -
______
4
->>> 4 in s -
______
False
->>> 4 in s[2] -
______
True
->>> s[2] + [3 + 2] -
______
[4, 0, 1, 5]
->>> 5 in s[2] -
______
False
->>> s[2] * 2 -
______
[4, 0, 1, 4, 0, 1]
->>> list(range(3, 6)) -
______
[3, 4, 5]
->>> range(3, 6) -
______
range(3, 6)
->>> r = range(3, 6) ->>> [r[0], r[2]] -
______
[3, 5]
->>> range(4)[-1] -
______
3
- - - - -

Q2: Print If

- - -

Implement print_if, which takes a list s and a one-argument function f. It prints each element x of s for which f(x) returns a true value.

- - - -
def print_if(s, f):
-    """Print each element of s for which f returns a true value.
-
-    >>> print_if([3, 4, 5, 6], lambda x: x > 4)
-    5
-    6
-    >>> result = print_if([3, 4, 5, 6], lambda x: x % 2 == 0)
-    4
-    6
-    >>> print(result)  # print_if should return None
-    None
-    """
-    for x in s:
-
if f(x): - print(x)
- -
- -

Use Ok to test your code:

python3 ok -q print_if
- -
- - -

Q3: Close

- - -

Implement close, which takes a list of numbers s and a non-negative integer k. It returns how many of the elements of s are within k of their index. That is, the absolute value of the difference between the element and its index is less than or equal to k.

- -

Remember that list is "zero-indexed"; the index of the first element is 0.

- - - -
def close(s, k):
-    """Return how many elements of s that are within k of their index.
-
-    >>> t = [6, 2, 4, 3, 5]
-    >>> close(t, 0)  # Only 3 is equal to its index
-    1
-    >>> close(t, 1)  # 2, 3, and 5 are within 1 of their index
-    3
-    >>> close(t, 2)  # 2, 3, 4, and 5 are all within 2 of their index
-    4
-    >>> close(list(range(10)), 0)
-    10
-    """
-    count = 0
-    for i in range(len(s)):  # Use a range to loop over indices
-
if abs(i - s[i]) <= k: - count += 1
return count
- -
- -

Use Ok to test your code:

python3 ok -q close
- -
- - -

List Comprehensions

- - - -

Q4: WWPD: List Comprehensions

- - -

Use Ok to test your knowledge with the following "What Would Python Display?" questions:

python3 ok -q list-comprehensions-wwpd -u
- -
- -

Predict what Python will display when you type the following into the -interactive interpreter. Then try it to check your answers.

- -
>>> [2 * x for x in range(4)]
-
______
[0, 2, 4, 6]
->>> [y for y in [6, 1, 6, 1] if y > 2] -
______
[6, 6]
->>> [[1] + s for s in [[4], [5, 6]]] -
______
[[1, 4], [1, 5, 6]]
->>> [z + 1 for z in range(10) if z % 3 == 0] -
______
[1, 4, 7, 10]
- - - - -

Q5: Close List

- - -

Implement close_list, which takes a list of numbers s and a non-negative integer k. It returns a list of the elements of s that are within k of their index. That is, the absolute value of the difference between the element and its index is less than or equal to k.

- - - -
def close_list(s, k):
-    """Return a list of the elements of s that are within k of their index.
-
-    >>> t = [6, 2, 4, 3, 5]
-    >>> close_list(t, 0)  # Only 3 is equal to its index
-    [3]
-    >>> close_list(t, 1)  # 2, 3, and 5 are within 1 of their index
-    [2, 3, 5]
-    >>> close_list(t, 2)  # 2, 3, 4, and 5 are all within 2 of their index
-    [2, 4, 3, 5]
-    """
-
return [s[i] for i in range(len(s)) if abs(i - s[i]) <= k]
- -
- -

Use Ok to test your code:

python3 ok -q close_list
- -
- - -

Q6: Squares Only

- - -

Implement the function squares, which takes in a list of positive integers. -It returns a list that contains the square roots of the elements of the original -list that are perfect squares. Use a list comprehension.

- -

To find if x is a perfect square, you can check if sqrt(x) equals round(sqrt(x)).

- - - -
from math import sqrt
-
-def squares(s):
-    """Returns a new list containing square roots of the elements of the
-    original list that are perfect squares.
-
-    >>> seq = [8, 49, 8, 9, 2, 1, 100, 102]
-    >>> squares(seq)
-    [7, 3, 1, 10]
-    >>> seq = [500, 30]
-    >>> squares(seq)
-    []
-    """
-
return [round(n ** 0.5) for n in s if n == round(n ** 0.5) ** 2]
- -
- -

It might be helpful to construct a skeleton list comprehension to begin -with:

- -
[round(sqrt(x)) for x in s if is_perfect_square(x)]
- -

This is great, but it requires that we have an is_perfect_square -function. How might we check if something is a perfect square?

- -
    -
  • If the square root of a number is a whole number, then it is a perfect - square. For example, sqrt(61) = 7.81024... (not a perfect square) and - sqrt(49) = 7 (perfect square).
  • -
  • Once we obtain the square root of the number, we just need to check if - something is a whole number. The is_perfect_square function might - look like:

    - -
    def is_perfect_square(x):
    -    return is_whole(sqrt(x))
  • -
  • One last piece of the puzzle: to check if a number is whole, we just - need to see if it has a decimal or not. The way we've chosen to do it in - the solution is to compare the original number to the round version - (thus removing all decimals), but a technique employing floor division - (//) or something else entirely could work too.
  • -
- -

We've written all these helper functions to solve this problem, but they -are actually all very short. Therefore, we can just copy the body of each -into the original list comprehension, arriving at the solution we finally -present.

- -

Video walkthrough:

YouTube link

- -
-
- -

Use Ok to test your code:

python3 ok -q squares
- -
- - -

Recursion

- - - -

Q7: Double Eights

- - -

Write a recursive function that takes in a positive integer n and determines if its -digits contain two adjacent 8s (that is, two 8s right next to each other). Do not use for or while.

- -

Hint: Start by coming up with a recursive plan: the digits of a number have double eights if either (think of something that is straightforward to check) or double eights appear in the rest of the digits.

- - - -
def double_eights(n):
-    """ Returns whether or not n has two digits in row that
-    are the number 8. Assume n has at least two digits in it.
-
-    >>> double_eights(1288)
-    True
-    >>> double_eights(880)
-    True
-    >>> double_eights(538835)
-    True
-    >>> double_eights(284682)
-    False
-    >>> double_eights(588138)
-    True
-    >>> double_eights(78)
-    False
-    >>> from construct_check import check
-    >>> # ban iteration
-    >>> check(LAB_SOURCE_FILE, 'double_eights', ['While', 'For'])
-    True
-    """
-
last, second_last = n % 10, n // 10 % 10 - if last == 8 and second_last == 8: - return True - elif n < 100: - return False - return double_eights(n // 10) - - # Alternate solution - last, second_last = n % 10, n // 10 % 10 - if n < 10: - return False - return (last == 8 and second_last == 8) or double_eights(n // 10) - - # Alternate solution with helper function: - def helper(num, prev_eight): - if num == 0: - return False - if num % 10 == 8: - if prev_eight: - return True - return helper(num // 10, True) - return helper(num // 10, False) - return helper(n, False)
- -
- -

Use Ok to test your code:

python3 ok -q double_eights
- -
- - -

Q8: Making Onions

- - -

Write a function make_onion that takes in two one-argument functions, f and -g. It returns a function that takes in three arguments: x, y, and -limit. The returned function returns True if it is possible to reach y -from x using up to limit calls to f and g, and False otherwise.

- -

For example, if f adds 1 and g doubles, then it is possible to reach 25 from -5 in four calls: f(g(g(f(5)))).

- - - -
def make_onion(f, g):
-    """Return a function can_reach(x, y, limit) that returns
-    whether some call expression containing only f, g, and x with
-    up to limit calls will give the result y.
-
-    >>> up = lambda x: x + 1
-    >>> double = lambda y: y * 2
-    >>> can_reach = make_onion(up, double)
-    >>> can_reach(5, 25, 4)      # 25 = up(double(double(up(5))))
-    True
-    >>> can_reach(5, 25, 3)      # Not possible
-    False
-    >>> can_reach(1, 1, 0)      # 1 = 1
-    True
-    >>> add_ing = lambda x: x + "ing"
-    >>> add_end = lambda y: y + "end"
-    >>> can_reach_string = make_onion(add_ing, add_end)
-    >>> can_reach_string("cry", "crying", 1)      # "crying" = add_ing("cry")
-    True
-    >>> can_reach_string("un", "unending", 3)     # "unending" = add_ing(add_end("un"))
-    True
-    >>> can_reach_string("peach", "folding", 4)   # Not possible
-    False
-    """
-    def can_reach(x, y, limit):
-        if limit < 0:
-
return False
elif x == y: -
return True
else: -
return can_reach(f(x), y, limit - 1) or can_reach(g(x), y, limit - 1)
return can_reach
- -
- -

Use Ok to test your code:

python3 ok -q make_onion
- -
- - -

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.

- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/lab/sol-lab03/lab03.ok b/lab/sol-lab03/lab03.ok deleted file mode 100644 index 091c8cffc4..0000000000 --- a/lab/sol-lab03/lab03.ok +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "Lab 3", - "endpoint": "cal/cs61a/fa24/lab03", - "src": [ - "lab03.py" - ], - "tests": { - "lab*.py": "doctest", - "tests/*.py": "ok_test" - }, - "default_tests": [ - "lists-wwpd", - "print_if", - "close", - "list-comprehensions-wwpd", - "close_list", - "squares", - "double_eights", - "make_onion" - ], - "protocols": [ - "restore", - "file_contents", - "unlock", - "grading", - "analytics", - "backup" - ] -} \ No newline at end of file diff --git a/lab/sol-lab03/lab03.py b/lab/sol-lab03/lab03.py deleted file mode 100644 index b2a61fec9a..0000000000 --- a/lab/sol-lab03/lab03.py +++ /dev/null @@ -1,149 +0,0 @@ -LAB_SOURCE_FILE=__file__ - - -def print_if(s, f): - """Print each element of s for which f returns a true value. - - >>> print_if([3, 4, 5, 6], lambda x: x > 4) - 5 - 6 - >>> result = print_if([3, 4, 5, 6], lambda x: x % 2 == 0) - 4 - 6 - >>> print(result) # print_if should return None - None - """ - for x in s: - if f(x): - print(x) - - -def close(s, k): - """Return how many elements of s that are within k of their index. - - >>> t = [6, 2, 4, 3, 5] - >>> close(t, 0) # Only 3 is equal to its index - 1 - >>> close(t, 1) # 2, 3, and 5 are within 1 of their index - 3 - >>> close(t, 2) # 2, 3, 4, and 5 are all within 2 of their index - 4 - >>> close(list(range(10)), 0) - 10 - """ - count = 0 - for i in range(len(s)): # Use a range to loop over indices - if abs(i - s[i]) <= k: - count += 1 - return count - - -def close_list(s, k): - """Return a list of the elements of s that are within k of their index. - - >>> t = [6, 2, 4, 3, 5] - >>> close_list(t, 0) # Only 3 is equal to its index - [3] - >>> close_list(t, 1) # 2, 3, and 5 are within 1 of their index - [2, 3, 5] - >>> close_list(t, 2) # 2, 3, 4, and 5 are all within 2 of their index - [2, 4, 3, 5] - """ - return [s[i] for i in range(len(s)) if abs(i - s[i]) <= k] - - -from math import sqrt - -def squares(s): - """Returns a new list containing square roots of the elements of the - original list that are perfect squares. - - >>> seq = [8, 49, 8, 9, 2, 1, 100, 102] - >>> squares(seq) - [7, 3, 1, 10] - >>> seq = [500, 30] - >>> squares(seq) - [] - """ - return [round(n ** 0.5) for n in s if n == round(n ** 0.5) ** 2] - - -def double_eights(n): - """ Returns whether or not n has two digits in row that - are the number 8. Assume n has at least two digits in it. - - >>> double_eights(1288) - True - >>> double_eights(880) - True - >>> double_eights(538835) - True - >>> double_eights(284682) - False - >>> double_eights(588138) - True - >>> double_eights(78) - False - >>> from construct_check import check - >>> # ban iteration - >>> check(LAB_SOURCE_FILE, 'double_eights', ['While', 'For']) - True - """ - last, second_last = n % 10, n // 10 % 10 - if last == 8 and second_last == 8: - return True - elif n < 100: - return False - return double_eights(n // 10) - - # Alternate solution - last, second_last = n % 10, n // 10 % 10 - if n < 10: - return False - return (last == 8 and second_last == 8) or double_eights(n // 10) - - # Alternate solution with helper function: - def helper(num, prev_eight): - if num == 0: - return False - if num % 10 == 8: - if prev_eight: - return True - return helper(num // 10, True) - return helper(num // 10, False) - return helper(n, False) - - -def make_onion(f, g): - """Return a function can_reach(x, y, limit) that returns - whether some call expression containing only f, g, and x with - up to limit calls will give the result y. - - >>> up = lambda x: x + 1 - >>> double = lambda y: y * 2 - >>> can_reach = make_onion(up, double) - >>> can_reach(5, 25, 4) # 25 = up(double(double(up(5)))) - True - >>> can_reach(5, 25, 3) # Not possible - False - >>> can_reach(1, 1, 0) # 1 = 1 - True - >>> add_ing = lambda x: x + "ing" - >>> add_end = lambda y: y + "end" - >>> can_reach_string = make_onion(add_ing, add_end) - >>> can_reach_string("cry", "crying", 1) # "crying" = add_ing("cry") - True - >>> can_reach_string("un", "unending", 3) # "unending" = add_ing(add_end("un")) - True - >>> can_reach_string("peach", "folding", 4) # Not possible - False - """ - def can_reach(x, y, limit): - if limit < 0: - return False - elif x == y: - return True - else: - return can_reach(f(x), y, limit - 1) or can_reach(g(x), y, limit - 1) - return can_reach - diff --git a/lab/sol-lab03/lab03.zip b/lab/sol-lab03/lab03.zip deleted file mode 100644 index ac45451b74..0000000000 Binary files a/lab/sol-lab03/lab03.zip and /dev/null differ diff --git a/lab/sol-lab03/ok b/lab/sol-lab03/ok deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/lab/sol-lab03/ok and /dev/null differ diff --git a/lab/sol-lab03/tests/list-comprehensions-wwpd.py b/lab/sol-lab03/tests/list-comprehensions-wwpd.py deleted file mode 100644 index 15667a3dc8..0000000000 --- a/lab/sol-lab03/tests/list-comprehensions-wwpd.py +++ /dev/null @@ -1,23 +0,0 @@ -test = { - 'name': 'Comprehensions', - 'points': 0, - 'suites': [ - { - 'type': 'wwpp', - 'cases': [ - { - 'code': """ - >>> [2 * x for x in range(4)] - [0, 2, 4, 6] - >>> [y for y in [6, 1, 6, 1] if y > 2] - [6, 6] - >>> [[1] + s for s in [[4], [5, 6]]] - [[1, 4], [1, 5, 6]] - >>> [z + 1 for z in range(10) if z % 3 == 0] - [1, 4, 7, 10] - """ - } - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab03/tests/lists-wwpd.py b/lab/sol-lab03/tests/lists-wwpd.py deleted file mode 100644 index 42dacbc368..0000000000 --- a/lab/sol-lab03/tests/lists-wwpd.py +++ /dev/null @@ -1,43 +0,0 @@ -test = { - 'name': 'Lists', - 'points': 0, - 'suites': [ - { - 'type': 'wwpp', - 'cases': [ - { - 'code': """ - >>> s = [7//3, 5, [4, 0, 1], 2] - >>> s[0] - 2 - >>> s[2] - [4, 0, 1] - >>> s[-1] - 2 - >>> len(s) - 4 - >>> 4 in s - False - >>> 4 in s[2] - True - >>> s[2] + [3 + 2] - [4, 0, 1, 5] - >>> 5 in s[2] - False - >>> s[2] * 2 - [4, 0, 1, 4, 0, 1] - >>> list(range(3, 6)) - [3, 4, 5] - >>> range(3, 6) - range(3, 6) - >>> r = range(3, 6) - >>> [r[0], r[2]] - [3, 5] - >>> range(4)[-1] - 3 - """ - } - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab03/tests/make-onion.py b/lab/sol-lab03/tests/make-onion.py deleted file mode 100644 index cde0ea4254..0000000000 --- a/lab/sol-lab03/tests/make-onion.py +++ /dev/null @@ -1,32 +0,0 @@ -def make_onion(f, g): - """Return a function can_reach(x, y, limit) that returns - whether some call expression containing only f, g, and x with - up to limit calls will give the result y. - - >>> up = lambda x: x + 1 - >>> double = lambda y: y * 2 - >>> can_reach = make_onion(up, double) - >>> can_reach(5, 25, 4) # 25 = up(double(double(up(5)))) - True - >>> can_reach(5, 25, 3) # Not possible - False - >>> can_reach(1, 1, 0) # 1 = 1 - True - >>> add_ing = lambda x: x + "ing" - >>> add_end = lambda y: y + "end" - >>> can_reach_string = make_onion(add_ing, add_end) - >>> can_reach_string("cry", "crying", 1) # "crying" = add_ing("cry") - True - >>> can_reach_string("un", "unending", 3) # "unending" = add_ing(add_end("un")) - True - >>> can_reach_string("peach", "folding", 4) # Not possible - False - """ - def can_reach(x, y, limit): - if limit < 0: - return False - elif x == y: - return True - else: - return can_reach(f(x), y, limit - 1) or can_reach(g(x), y, limit - 1) - return can_reach \ No newline at end of file diff --git a/lab/sol-lab04/construct_check.py b/lab/sol-lab04/construct_check.py deleted file mode 100644 index bee096e7e4..0000000000 --- a/lab/sol-lab04/construct_check.py +++ /dev/null @@ -1,180 +0,0 @@ -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/lab/sol-lab04/index.html b/lab/sol-lab04/index.html deleted file mode 100644 index d03956458c..0000000000 --- a/lab/sol-lab04/index.html +++ /dev/null @@ -1,705 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Lab 4 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Lab 4 Solutions - - - - - - -

-
- -

Solution Files

- - - -

Required Questions

- - - -

Dictionaries

- - -

Consult the drop-down if you need a refresher on dictionaries. It's -okay to skip directly to the questions and refer back -here should you get stuck.

- - - -
- -

A dictionary contains key-value pairs and allows the values to be looked up by -their key using square brackets. Each key must be unique.

- -
>>> d = {2: 4, 'two': ['four'], (1, 1): 4}
->>> d[2]
-4
->>> d['two']
-['four']
->>> d[(1, 1)]
-4
- -

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:

python3 ok -q pokemon -u
- -
- -
>>> pokemon = {'pikachu': 25, 'dragonair': 148, 'mew': 151}
->>> pokemon['pikachu']
-
______
25
->>> len(pokemon) -
______
3
->>> 'mewtwo' in pokemon -
______
False
->>> 'pikachu' in pokemon -
______
True
->>> 25 in pokemon -
______
False
->>> 148 in pokemon.values() -
______
True
->>> 151 in pokemon.keys() -
______
False
->>> 'mew' in pokemon.keys() -
______
True
- - - - -

Q2: Divide

- - -

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 buy, which takes a list of required_fruits (strings), a dictionary - prices (strings for key, positive integers for value), and a total_amount (integer). It -prints all the ways to buy some of each required fruit so that the total price -equals total_amount. You must include at least one of every fruit in required_fruit -and cannot include any other fruits that are not in required_fruit.

- -

The display function will be helpful. You can call display on a fruit and its count to create a string containing both.

- -

What does fruits and amount represent? How are they used in the recursive?

- - - -
def buy(required_fruits, 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)
-    [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):
add(fruits[1:], amount - price * k, cart + display(fruit, k))
add(required_fruits, 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]' - """ - 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:

- -
>>> berkeley = make_city('Berkeley', 122, 37)
->>> get_name(berkeley)
-'Berkeley'
->>> get_lat(berkeley)
-122
->>> new_york = make_city('New York City', 74, 40)
->>> get_lon(new_york)
-40
- -

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!

- - - -
from math import sqrt
-def distance(city_a, city_b):
-    """
-    >>> city_a = make_city('city_a', 0, 1)
-    >>> city_b = make_city('city_b', 0, 2)
-    >>> distance(city_a, city_b)
-    1.0
-    >>> city_c = make_city('city_c', 6.5, 12)
-    >>> city_d = make_city('city_d', 2.5, 15)
-    >>> distance(city_c, city_d)
-    5.0
-    """
-
lat_1, lon_1 = get_lat(city_a), get_lon(city_a) - lat_2, lon_2 = get_lat(city_b), get_lon(city_b) - return sqrt((lat_1 - lat_2)**2 + (lon_1 - lon_2)**2)
- -
- -

Use Ok to test your code:

python3 ok -q distance
- -
- - -

Q5: Closer City

- - -

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'
-    """
-
new_city = make_city('arb', lat, lon) - dist1 = distance(city_a, new_city) - dist2 = distance(city_b, new_city) - if dist1 < dist2: - return get_name(city_a) - return get_name(city_b)
- -
- -

Use Ok to test your code:

python3 ok -q closer_city
- -
- - -

Q6: Don't violate the abstraction barrier!

- - -

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.

- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/lab/sol-lab04/lab04.ok b/lab/sol-lab04/lab04.ok deleted file mode 100644 index cbec3cd169..0000000000 --- a/lab/sol-lab04/lab04.ok +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "Lab 4", - "endpoint": "cal/cs61a/fa24/lab04", - "src": [ - "lab04.py" - ], - "tests": { - "lab*.py": "doctest", - "tests/*.py": "ok_test" - }, - "default_tests": [ - "pokemon", - "divide", - "buy", - "distance", - "closer_city", - "check_city_abstraction" - ], - "protocols": [ - "restore", - "file_contents", - "unlock", - "grading", - "analytics", - "backup" - ] -} \ No newline at end of file diff --git a/lab/sol-lab04/lab04.py b/lab/sol-lab04/lab04.py deleted file mode 100644 index ddf78cabfb..0000000000 --- a/lab/sol-lab04/lab04.py +++ /dev/null @@ -1,178 +0,0 @@ -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} - - -def buy(required_fruits, 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) - [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): - add(fruits[1:], amount - price * k, cart + display(fruit, k)) - add(required_fruits, 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]' - """ - assert count >= 1 and fruit[-1] == 's' - if count == 1: - fruit = fruit[:-1] # get rid of the plural s - return '[' + str(count) + ' ' + fruit + ']' - - - - -from math import sqrt -def distance(city_a, city_b): - """ - >>> city_a = make_city('city_a', 0, 1) - >>> city_b = make_city('city_b', 0, 2) - >>> distance(city_a, city_b) - 1.0 - >>> city_c = make_city('city_c', 6.5, 12) - >>> city_d = make_city('city_d', 2.5, 15) - >>> distance(city_c, city_d) - 5.0 - """ - lat_1, lon_1 = get_lat(city_a), get_lon(city_a) - lat_2, lon_2 = get_lat(city_b), get_lon(city_b) - return sqrt((lat_1 - lat_2)**2 + (lon_1 - lon_2)**2) - -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' - """ - new_city = make_city('arb', lat, lon) - dist1 = distance(city_a, new_city) - dist2 = distance(city_b, new_city) - if dist1 < dist2: - return get_name(city_a) - return get_name(city_b) - -def check_city_abstraction(): - """ - There's nothing for you to do for this function, it's just here for the extra doctest - >>> change_abstraction(True) - >>> city_a = make_city('city_a', 0, 1) - >>> city_b = make_city('city_b', 0, 2) - >>> distance(city_a, city_b) - 1.0 - >>> city_c = make_city('city_c', 6.5, 12) - >>> city_d = make_city('city_d', 2.5, 15) - >>> distance(city_c, city_d) - 5.0 - >>> 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' - >>> change_abstraction(False) - """ - -# Treat all the following code as being behind an abstraction layer, -# you shouldn't need to look at it. -def make_city(name, lat, lon): - """ - >>> city = make_city('Berkeley', 0, 1) - >>> get_name(city) - 'Berkeley' - >>> get_lat(city) - 0 - >>> get_lon(city) - 1 - """ - if change_abstraction.changed: - return {"name" : name, "lat" : lat, "lon" : lon} - else: - return [name, lat, lon] - -def get_name(city): - """ - >>> city = make_city('Berkeley', 0, 1) - >>> get_name(city) - 'Berkeley' - """ - if change_abstraction.changed: - return city["name"] - else: - return city[0] - -def get_lat(city): - """ - >>> city = make_city('Berkeley', 0, 1) - >>> get_lat(city) - 0 - """ - if change_abstraction.changed: - return city["lat"] - else: - return city[1] - -def get_lon(city): - """ - >>> city = make_city('Berkeley', 0, 1) - >>> get_lon(city) - 1 - """ - if change_abstraction.changed: - return city["lon"] - else: - return city[2] - -############### - - -def change_abstraction(change): - """ - For testing purposes. - >>> change_abstraction(True) - >>> change_abstraction.changed - True - """ - change_abstraction.changed = change - -change_abstraction.changed = False - diff --git a/lab/sol-lab04/lab04.zip b/lab/sol-lab04/lab04.zip deleted file mode 100644 index 2a3ba8eabf..0000000000 Binary files a/lab/sol-lab04/lab04.zip and /dev/null differ diff --git a/lab/sol-lab04/ok b/lab/sol-lab04/ok deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/lab/sol-lab04/ok and /dev/null differ diff --git a/lab/sol-lab04/tests/lists-wwpd.py b/lab/sol-lab04/tests/lists-wwpd.py deleted file mode 100644 index 42dacbc368..0000000000 --- a/lab/sol-lab04/tests/lists-wwpd.py +++ /dev/null @@ -1,43 +0,0 @@ -test = { - 'name': 'Lists', - 'points': 0, - 'suites': [ - { - 'type': 'wwpp', - 'cases': [ - { - 'code': """ - >>> s = [7//3, 5, [4, 0, 1], 2] - >>> s[0] - 2 - >>> s[2] - [4, 0, 1] - >>> s[-1] - 2 - >>> len(s) - 4 - >>> 4 in s - False - >>> 4 in s[2] - True - >>> s[2] + [3 + 2] - [4, 0, 1, 5] - >>> 5 in s[2] - False - >>> s[2] * 2 - [4, 0, 1, 4, 0, 1] - >>> list(range(3, 6)) - [3, 4, 5] - >>> range(3, 6) - range(3, 6) - >>> r = range(3, 6) - >>> [r[0], r[2]] - [3, 5] - >>> range(4)[-1] - 3 - """ - } - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab04/tests/pokemon.py b/lab/sol-lab04/tests/pokemon.py deleted file mode 100644 index a039a847ba..0000000000 --- a/lab/sol-lab04/tests/pokemon.py +++ /dev/null @@ -1,50 +0,0 @@ -test = { - "name": "Dictionaries", - "points": 0, - "suites": [ - { - "type": "wwpp", - "cases": [ - { - "code": """ - >>> pokemon = {'pikachu': 25, 'dragonair': 148, 'mew': 151} - >>> pokemon['pikachu'] - 25 - >>> len(pokemon) - 3 - >>> 'mewtwo' in pokemon - False - >>> 'pikachu' in pokemon - True - >>> 25 in pokemon - False - >>> 148 in pokemon.values() - True - >>> 151 in pokemon.keys() - False - >>> 'mew' in pokemon.keys() - True - """, - } - ], - }, - # { - # "type": "wwpp", - # "cases": [ - # { - # "code": """ - # >>> letters = {'a': 1, 'b': 2, 'c': 3} - # >>> 'a' in letters - # True - # >>> 2 in letters - # False - # >>> sorted(list(letters.keys())) - # ['a', 'b', 'c'] - # >>> sorted(list(letters.items())) - # [('a', 1), ('b', 2), ('c', 3)] - # """, - # } - # ], - # }, - ], -} \ No newline at end of file diff --git a/lab/sol-lab04/tree.py b/lab/sol-lab04/tree.py deleted file mode 100644 index 15c2d26187..0000000000 --- a/lab/sol-lab04/tree.py +++ /dev/null @@ -1,65 +0,0 @@ - -# 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)]) \ No newline at end of file diff --git a/lab/sol-lab05/change_abstraction.py b/lab/sol-lab05/change_abstraction.py deleted file mode 100644 index 5a6fa5fc04..0000000000 --- a/lab/sol-lab05/change_abstraction.py +++ /dev/null @@ -1,10 +0,0 @@ -def change_abstraction(change): - """ - For testing purposes. - >>> change_abstraction(True) - >>> change_abstraction.changed - True - """ - change_abstraction.changed = change - -change_abstraction.changed = False \ No newline at end of file diff --git a/lab/sol-lab05/index.html b/lab/sol-lab05/index.html deleted file mode 100644 index 476145217e..0000000000 --- a/lab/sol-lab05/index.html +++ /dev/null @@ -1,963 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Lab 5 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Lab 5 Solutions - - - - - - -

-
- -

Solution Files

- - - -

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.

- - -

YouTube link

-
- - -

Mutability

- - -

Consult the drop-down if you need a refresher on mutability. It's -okay to skip directly to the questions and refer back -here should you get stuck.

- - - -
- -

Some objects in Python, such as lists and dictionaries, are mutable, -meaning that their contents or state can be changed. -Other objects, such as numeric types, tuples, and strings, are immutable, -meaning they cannot be changed once they are created.

- -

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.
  • -
- -

Dictionaries also have item assignment (often used) and pop (rarely used).

- -
>>> d = {2: 3, 4: 16}
->>> d[2] = 4
->>> d[3] = 9
->>> d
-{2: 4, 4: 16, 3: 9}
->>> d.pop(4)
-16
->>> d
-{2: 4, 3: 9}
- -
- - -

Q1: WWPD: List-Mutation

- - -

Important: -For all WWPD questions, type Function if you believe the answer is -<function...>, Error if it errors, and Nothing if nothing is displayed.

- - - -

Use Ok to test your knowledge with the following "What Would Python Display?" questions:

python3 ok -q list-mutation -u
- -
- -
>>> s = [6, 7, 8]
->>> print(s.append(6))
-
______
None
->>> s -
______
[6, 7, 8, 6]
->>> s.insert(0, 9) ->>> s -
______
[9, 6, 7, 8, 6]
->>> x = s.pop(1) ->>> s -
______
[9, 7, 8, 6]
->>> s.remove(x) ->>> s -
______
[9, 7, 8]
->>> a, b = s, s[:] ->>> a is s -
______
True
->>> b == s -
______
True
->>> b is s -
______
False
->>> a.pop() -
______
8
->>> a + b -
______
[9, 7, 9, 7, 8]
->>> s = [3] ->>> s.extend([4, 5]) ->>> s -
______
[3, 4, 5]
->>> a -
______
[9, 7]
->>> s.extend([s.append(9), s.append(10)]) ->>> s -
______
[3, 4, 5, 9, 10, None, None]
- - - - -

Q2: Insert Items

- - -

Write a function which takes in a list s, a value before, and a value -after. It inserts after just after each value equal to before in s. It -returns s.

- -

Important: No new lists should be created or returned.

- -

Note: -If the values passed into before and after are equal, make -sure you're not creating an infinitely long list while iterating through it. -If you find that your code is taking more than a few seconds to run, the -function may be in an infinite loop of inserting new values.

- - - -
def insert_items(s, before, after):
-    """Insert after into s after each occurrence of before and then return s.
-
-    >>> test_s = [1, 5, 8, 5, 2, 3]
-    >>> new_s = insert_items(test_s, 5, 7)
-    >>> new_s
-    [1, 5, 7, 8, 5, 7, 2, 3]
-    >>> test_s
-    [1, 5, 7, 8, 5, 7, 2, 3]
-    >>> new_s is test_s
-    True
-    >>> double_s = [1, 2, 1, 2, 3, 3]
-    >>> double_s = insert_items(double_s, 3, 4)
-    >>> double_s
-    [1, 2, 1, 2, 3, 4, 3, 4]
-    >>> large_s = [1, 4, 8]
-    >>> large_s2 = insert_items(large_s, 4, 4)
-    >>> large_s2
-    [1, 4, 4, 8]
-    >>> large_s3 = insert_items(large_s2, 4, 6)
-    >>> large_s3
-    [1, 4, 6, 4, 6, 8]
-    >>> large_s3 is large_s
-    True
-    """
-
index = 0 - while index < len(s): - if s[index] == before: - s.insert(index + 1, after) - index += 1 - index += 1 - return s - - # Alternate solution (backward traversal): - i = len(s) - 1 - while i >= 0: - if s[i] == before: - s.insert(i + 1, after) - i -= 1 - return s
- -
- -

Use Ok to test your code:

python3 ok -q insert_items
- -
- - - - - - - - -

Q3: Group By

- -

Write a function that takes in a list s and a function fn, and returns a dictionary.

- -

In this dictionary, each key should be the result of applying fn to the elements in s, and each corresponding value should be a list of elements from s that produce the same result when fn is applied to them.

- -

For example, if an element e in s results in fn(e), then e should be added to the list corresponding to the key fn(e) in the dictionary.

- - - -
def group_by(s, fn):
-    """Return a dictionary of lists that together contain the elements of s.
-    The key for each list is the value that fn returns when called on any of the
-    values of that list.
-
-    >>> group_by([12, 23, 14, 45], lambda p: p // 10)
-    {1: [12, 14], 2: [23], 4: [45]}
-    >>> group_by(range(-3, 4), lambda x: x * x)
-    {9: [-3, 3], 4: [-2, 2], 1: [-1, 1], 0: [0]}
-    """
-    grouped = {}
-
for x in s:
key = fn(x)
if key in grouped: -
grouped[key].append(x)
else: -
grouped[key] = [x]
return grouped
- -
- -

Use Ok to test your code:

python3 ok -q group_by
- -
- -
- -

This is a dictionary question! The primary hard part about it is -understanding the two cases of the key being present or not.

- -

One thing that might be useful to tell students once they’re done is -that in practice you probably want to use -collections.defaultdict(list) for this.

- -
- - -

Iterators

- - -

Consult the drop-down if you need a refresher on iterators. It's -okay to skip directly to the questions and refer back -here should you get stuck.

- - - -
- -

An iterable is any value that can be iterated through, or gone through one -element at a time. One construct that we've used to iterate through an iterable -is a for statement:

- -
for elem in iterable:
-    # do something
- -

In general, an iterable is an object on which calling the built-in iter -function returns an iterator. An iterator is an object on which calling -the built-in next function returns the next value.

- -

For example, a list is an iterable value.

- -
>>> s = [1, 2, 3, 4]
->>> next(s)       # s is iterable, but not an iterator
-TypeError: 'list' object is not an iterator
->>> t = iter(s)   # Creates an iterator
->>> t
-<list_iterator object ...>
->>> next(t)       # Calling next on an iterator
-1
->>> next(t)       # Calling next on the same iterator
-2
->>> next(iter(t)) # Calling iter on an iterator returns itself
-3
->>> t2 = iter(s)
->>> next(t2)      # Second iterator starts at the beginning of s
-1
->>> next(t)       # First iterator is unaffected by second iterator
-4
->>> next(t)       # No elements left!
-StopIteration
->>> s             # Original iterable is unaffected
-[1, 2, 3, 4]
- -

You can also use an iterator in a for statement because all iterators are -iterable. But note that since iterators keep their state, they're -only good to iterate through an iterable once:

- -
>>> t = iter([4, 3, 2, 1])
->>> for e in t:
-...     print(e)
-4
-3
-2
-1
->>> for e in t:
-...     print(e)
- -

There are built-in functions that return iterators.

- -
>>> m = map(lambda x: x * x, [3, 4, 5])
->>> next(m)
-9
->>> next(m)
-16
->>> f = filter(lambda x: x > 3, [3, 4, 5])
->>> next(f)
-4
->>> next(f)
-5
->>> z = zip([30, 40, 50], [3, 4, 5])
->>> next(z)
-(30, 3)
->>> next(z)
-(40, 4)
- -
- - -

Q4: WWPD: Iterators

- - -

Important: -Enter StopIteration if a StopIteration exception occurs, -Error if you believe a different error occurs, -and Iterator if the output is an iterator object.

- -

Important: Python's built-in function map, filter, and zip return iterators, not lists.

- - - -

Use Ok to test your knowledge with the following "What Would Python Display?" questions:

python3 ok -q iterators-wwpd -u
- -
- -
>>> s = [1, 2, 3, 4]
->>> t = iter(s)
->>> next(s)
-
______
Error
->>> next(t) -
______
1
->>> next(t) -
______
2
->>> next(iter(s)) -
______
1
->>> next(iter(s)) -
______
1
->>> u = t ->>> next(u) -
______
3
->>> next(t) -
______
4
- - - -
>>> r = range(6)
->>> r_iter = iter(r)
->>> next(r_iter)
-
______
0
->>> [x + 1 for x in r] -
______
[1, 2, 3, 4, 5, 6]
->>> [x + 1 for x in r_iter] -
______
[2, 3, 4, 5, 6]
->>> next(r_iter) -
______
StopIteration
- - - -
>>> map_iter = map(lambda x : x + 10, range(5))
->>> next(map_iter)
-
______
10
->>> next(map_iter) -
______
11
->>> list(map_iter) -
______
[12, 13, 14]
->>> for e in filter(lambda x : x % 4 == 0, range(1000, 1008)): -... print(e) -
______
1000 1004
->>> [x + y for x, y in zip([1, 2, 3], [4, 5, 6])] -
______
[5, 7, 9]
- - - - -

Q5: Count Occurrences

- - -

Implement count_occurrences, which takes an iterator t, an integer n, and -a value x. It returns the number of elements in the -first n elements of t that equal to x.

- -

Important: Call next on t exactly n times. Assume there are at least -n elements in t.

- - - - - -
def count_occurrences(t, n, x):
-    """Return the number of times that x is equal to one of the
-    first n elements of iterator t.
-
-    >>> s = iter([10, 9, 10, 9, 9, 10, 8, 8, 8, 7])
-    >>> count_occurrences(s, 10, 9)
-    3
-    >>> t = iter([10, 9, 10, 9, 9, 10, 8, 8, 8, 7])
-    >>> count_occurrences(t, 3, 10)
-    2
-    >>> u = iter([3, 2, 2, 2, 1, 2, 1, 4, 4, 5, 5, 5])
-    >>> count_occurrences(u, 1, 3)  # Only iterate over 3
-    1
-    >>> count_occurrences(u, 3, 2)  # Only iterate over 2, 2, 2
-    3
-    >>> list(u)                     # Ensure that the iterator has advanced the right amount
-    [1, 2, 1, 4, 4, 5, 5, 5]
-    >>> v = iter([4, 1, 6, 6, 7, 7, 6, 6, 2, 2, 2, 5])
-    >>> count_occurrences(v, 6, 6)
-    2
-    """
-
count = 0 - for _ in range(n): - if next(t) == x: - count += 1 - return count
- -
- -

Use Ok to test your code:

python3 ok -q count_occurrences
- -
- - -

Q6: Repeated

- - -

Implement repeated, which takes in an iterator t and an integer k greater -than 1. It returns the first value in t that appears k times in a row.

- -

Important: Call next on t only the minimum number of times required. -Assume that there is an element of t repeated at least k times in a row.

- -

Hint: -If you are receiving a StopIteration exception, -your repeated function is calling next too many times.

- - - -
def repeated(t, k):
-    """Return the first value in iterator t that appears k times in a row,
-    calling next on t as few times as possible.
-
-    >>> s = iter([10, 9, 10, 9, 9, 10, 8, 8, 8, 7])
-    >>> repeated(s, 2)
-    9
-    >>> t = iter([10, 9, 10, 9, 9, 10, 8, 8, 8, 7])
-    >>> repeated(t, 3)
-    8
-    >>> u = iter([3, 2, 2, 2, 1, 2, 1, 4, 4, 5, 5, 5])
-    >>> repeated(u, 3)
-    2
-    >>> repeated(u, 3)
-    5
-    >>> v = iter([4, 1, 6, 6, 7, 7, 8, 8, 2, 2, 2, 5])
-    >>> repeated(v, 3)
-    2
-    """
-    assert k > 1
-
count = 0 - last_item = None - while True: - item = next(t) - if item == last_item: - count += 1 - else: - last_item = item - count = 1 - if count == k: - return item
- -
- -

Use Ok to test your code:

python3 ok -q repeated
- -
- - -

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.

- - -

Optional Questions

- - -

These questions are optional. If you don't complete them, you will -still receive credit for lab. They are great practice, so do them -anyway!

- - -

Q7: Sprout Leaves

- - -

Define a function sprout_leaves that takes in a tree, t, and a list of -leaves, leaves. It produces a new tree that is identical to t, but where each -old leaf node has new branches, one for each leaf in leaves.

- -

For example, say we have the tree t = tree(1, [tree(2), tree(3, [tree(4)])]):

- -
  1
- / \
-2   3
-    |
-    4
- -

If we call sprout_leaves(t, [5, 6]), the result is the following tree:

- -
       1
-     /   \
-    2     3
-   / \    |
-  5   6   4
-         / \
-        5   6
- - - -
def sprout_leaves(t, leaves):
-    """Sprout new leaves containing the labels in leaves at each leaf of
-    the original tree t and return the resulting tree.
-
-    >>> t1 = tree(1, [tree(2), tree(3)])
-    >>> print_tree(t1)
-    1
-      2
-      3
-    >>> new1 = sprout_leaves(t1, [4, 5])
-    >>> print_tree(new1)
-    1
-      2
-        4
-        5
-      3
-        4
-        5
-
-    >>> t2 = tree(1, [tree(2, [tree(3)])])
-    >>> print_tree(t2)
-    1
-      2
-        3
-    >>> new2 = sprout_leaves(t2, [6, 1, 2])
-    >>> print_tree(new2)
-    1
-      2
-        3
-          6
-          1
-          2
-    """
-
if is_leaf(t): - return tree(label(t), [tree(leaf) for leaf in leaves]) - return tree(label(t), [sprout_leaves(s, leaves) for s in branches(t)])
- -
- -

Use Ok to test your code:

python3 ok -q sprout_leaves
- -
- - -

Q8: Partial Reverse

- - -

When working with lists, it is often useful to reverse the list. For example, -reversing the list [1, 2, 3, 4, 5] will give [5, 4, 3, 2, 1]. However, in -some situations, it may be more useful to only partially reverse the list and -keep some of its elements in the same order. For example, partially reversing -the list [1, 2, 3, 4, 5] starting from index 2 until the end of the list will -give [1, 2, 5, 4, 3].

- -

Implement the function partial_reverse which reverses a list starting -from start until the end of the list. This reversal should be -in-place, meaning that the original list is modified. Do not create a -new list inside your function, even if you do not return it. -The partial_reverse function returns None.

- -

Hint: You can swap elements at index i and j in list s with multiple -assignment: s[i], s[j] = s[j], s[i]

- - - -
def partial_reverse(s, start):
-    """Reverse part of a list in-place, starting with start up to the end of
-    the list.
-
-    >>> a = [1, 2, 3, 4, 5, 6, 7]
-    >>> partial_reverse(a, 2)
-    >>> a
-    [1, 2, 7, 6, 5, 4, 3]
-    >>> partial_reverse(a, 5)
-    >>> a
-    [1, 2, 7, 6, 5, 3, 4]
-    """
-
end = len(s) - 1 - while start < end: - s[start], s[end] = s[end], s[start] - start, end = start + 1, end - 1
- -
- -

Use Ok to test your code:

python3 ok -q partial_reverse
- -
- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/lab/sol-lab05/lab05.ok b/lab/sol-lab05/lab05.ok deleted file mode 100644 index 93bc5b9007..0000000000 --- a/lab/sol-lab05/lab05.ok +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "Lab 5", - "endpoint": "cal/cs61a/fa24/lab05", - "src": [ - "lab05.py" - ], - "tests": { - "lab*.py": "doctest", - "tests/*.py": "ok_test" - }, - "default_tests": [ - "list-mutation", - "insert_items", - "group_by", - "iterators-wwpd", - "count_occurrences", - "repeated" - ], - "protocols": [ - "restore", - "file_contents", - "analytics", - "unlock", - "grading", - "backup" - ] -} \ No newline at end of file diff --git a/lab/sol-lab05/lab05.py b/lab/sol-lab05/lab05.py deleted file mode 100644 index 369e8c0a46..0000000000 --- a/lab/sol-lab05/lab05.py +++ /dev/null @@ -1,248 +0,0 @@ -HW_SOURCE_FILE=__file__ - - -def insert_items(s, before, after): - """Insert after into s after each occurrence of before and then return s. - - >>> test_s = [1, 5, 8, 5, 2, 3] - >>> new_s = insert_items(test_s, 5, 7) - >>> new_s - [1, 5, 7, 8, 5, 7, 2, 3] - >>> test_s - [1, 5, 7, 8, 5, 7, 2, 3] - >>> new_s is test_s - True - >>> double_s = [1, 2, 1, 2, 3, 3] - >>> double_s = insert_items(double_s, 3, 4) - >>> double_s - [1, 2, 1, 2, 3, 4, 3, 4] - >>> large_s = [1, 4, 8] - >>> large_s2 = insert_items(large_s, 4, 4) - >>> large_s2 - [1, 4, 4, 8] - >>> large_s3 = insert_items(large_s2, 4, 6) - >>> large_s3 - [1, 4, 6, 4, 6, 8] - >>> large_s3 is large_s - True - """ - index = 0 - while index < len(s): - if s[index] == before: - s.insert(index + 1, after) - index += 1 - index += 1 - return s - - # Alternate solution (backward traversal): - i = len(s) - 1 - while i >= 0: - if s[i] == before: - s.insert(i + 1, after) - i -= 1 - return s - - -def group_by(s, fn): - """Return a dictionary of lists that together contain the elements of s. - The key for each list is the value that fn returns when called on any of the - values of that list. - - >>> group_by([12, 23, 14, 45], lambda p: p // 10) - {1: [12, 14], 2: [23], 4: [45]} - >>> group_by(range(-3, 4), lambda x: x * x) - {9: [-3, 3], 4: [-2, 2], 1: [-1, 1], 0: [0]} - """ - grouped = {} - for x in s: - key = fn(x) - if key in grouped: - grouped[key].append(x) - else: - grouped[key] = [x] - return grouped - - -def count_occurrences(t, n, x): - """Return the number of times that x is equal to one of the - first n elements of iterator t. - - >>> s = iter([10, 9, 10, 9, 9, 10, 8, 8, 8, 7]) - >>> count_occurrences(s, 10, 9) - 3 - >>> t = iter([10, 9, 10, 9, 9, 10, 8, 8, 8, 7]) - >>> count_occurrences(t, 3, 10) - 2 - >>> u = iter([3, 2, 2, 2, 1, 2, 1, 4, 4, 5, 5, 5]) - >>> count_occurrences(u, 1, 3) # Only iterate over 3 - 1 - >>> count_occurrences(u, 3, 2) # Only iterate over 2, 2, 2 - 3 - >>> list(u) # Ensure that the iterator has advanced the right amount - [1, 2, 1, 4, 4, 5, 5, 5] - >>> v = iter([4, 1, 6, 6, 7, 7, 6, 6, 2, 2, 2, 5]) - >>> count_occurrences(v, 6, 6) - 2 - """ - count = 0 - for _ in range(n): - if next(t) == x: - count += 1 - return count - - -def repeated(t, k): - """Return the first value in iterator t that appears k times in a row, - calling next on t as few times as possible. - - >>> s = iter([10, 9, 10, 9, 9, 10, 8, 8, 8, 7]) - >>> repeated(s, 2) - 9 - >>> t = iter([10, 9, 10, 9, 9, 10, 8, 8, 8, 7]) - >>> repeated(t, 3) - 8 - >>> u = iter([3, 2, 2, 2, 1, 2, 1, 4, 4, 5, 5, 5]) - >>> repeated(u, 3) - 2 - >>> repeated(u, 3) - 5 - >>> v = iter([4, 1, 6, 6, 7, 7, 8, 8, 2, 2, 2, 5]) - >>> repeated(v, 3) - 2 - """ - assert k > 1 - count = 0 - last_item = None - while True: - item = next(t) - if item == last_item: - count += 1 - else: - last_item = item - count = 1 - if count == k: - return item - - -def sprout_leaves(t, leaves): - """Sprout new leaves containing the labels in leaves at each leaf of - the original tree t and return the resulting tree. - - >>> t1 = tree(1, [tree(2), tree(3)]) - >>> print_tree(t1) - 1 - 2 - 3 - >>> new1 = sprout_leaves(t1, [4, 5]) - >>> print_tree(new1) - 1 - 2 - 4 - 5 - 3 - 4 - 5 - - >>> t2 = tree(1, [tree(2, [tree(3)])]) - >>> print_tree(t2) - 1 - 2 - 3 - >>> new2 = sprout_leaves(t2, [6, 1, 2]) - >>> print_tree(new2) - 1 - 2 - 3 - 6 - 1 - 2 - """ - if is_leaf(t): - return tree(label(t), [tree(leaf) for leaf in leaves]) - return tree(label(t), [sprout_leaves(s, leaves) for s in branches(t)]) - - -def partial_reverse(s, start): - """Reverse part of a list in-place, starting with start up to the end of - the list. - - >>> a = [1, 2, 3, 4, 5, 6, 7] - >>> partial_reverse(a, 2) - >>> a - [1, 2, 7, 6, 5, 4, 3] - >>> partial_reverse(a, 5) - >>> a - [1, 2, 7, 6, 5, 3, 4] - """ - end = len(s) - 1 - while start < end: - s[start], s[end] = s[end], s[start] - start, end = start + 1, end - 1 - - - -# 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/lab/sol-lab05/lab05.zip b/lab/sol-lab05/lab05.zip deleted file mode 100644 index 3e1cb08729..0000000000 Binary files a/lab/sol-lab05/lab05.zip and /dev/null differ diff --git a/lab/sol-lab05/ok b/lab/sol-lab05/ok deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/lab/sol-lab05/ok and /dev/null differ diff --git a/lab/sol-lab05/tests/dictionaries-wwpd.py b/lab/sol-lab05/tests/dictionaries-wwpd.py deleted file mode 100644 index 657aaa7b92..0000000000 --- a/lab/sol-lab05/tests/dictionaries-wwpd.py +++ /dev/null @@ -1,49 +0,0 @@ -test = { - "name": "Dictionaries WWPD", - "points": 0, - "suites": [ - { - "type": "wwpp", - "cases": [ - { - "code": """ - >>> pokemon = {'pikachu': 25, 'dragonair': 148} - >>> pokemon - {'pikachu': 25, 'dragonair': 148} - >>> 'mewtwo' in pokemon - False - >>> len(pokemon) - 2 - >>> pokemon['mew'] = pokemon['pikachu'] #If this errors, just type Error - >>> pokemon[25] = 'pikachu' - >>> pokemon - {'pikachu': 25, 'dragonair': 148, 'mew': 25, 25: 'pikachu'} - >>> pokemon['mewtwo'] = pokemon['mew'] * 2 - >>> pokemon - {'pikachu': 25, 'dragonair': 148, 'mew': 25, 25: 'pikachu', 'mewtwo': 50} - >>> pokemon[['firetype', 'flying']] = 146 # If this errors, just type Error. Note that dictionary keys must be hashable. - Error - """, - } - ], - }, - # { - # "type": "wwpp", - # "cases": [ - # { - # "code": """ - # >>> letters = {'a': 1, 'b': 2, 'c': 3} - # >>> 'a' in letters - # True - # >>> 2 in letters - # False - # >>> sorted(list(letters.keys())) - # ['a', 'b', 'c'] - # >>> sorted(list(letters.items())) - # [('a', 1), ('b', 2), ('c', 3)] - # """, - # } - # ], - # }, - ], -} \ No newline at end of file diff --git a/lab/sol-lab05/tests/iterators-wwpd.py b/lab/sol-lab05/tests/iterators-wwpd.py deleted file mode 100644 index c87bd4c269..0000000000 --- a/lab/sol-lab05/tests/iterators-wwpd.py +++ /dev/null @@ -1,64 +0,0 @@ -test = { - 'name': 'Iterators', - 'points': 0, - 'suites': [ - { - 'type': 'wwpp', - 'cases': [ - { - 'code': """ - >>> # Enter StopIteration if StopIteration exception occurs, Error for other errors - >>> # Enter Iterator if the output is an iterator object. - >>> s = [1, 2, 3, 4] - >>> t = iter(s) - >>> next(s) - Error - >>> next(t) - 1 - >>> next(t) - 2 - >>> next(iter(s)) - 1 - >>> next(iter(s)) - 1 - >>> next(t) - 3 - >>> next(t) - 4 - """, - }, - { - 'code': """ - >>> r = range(6) - >>> r_iter = iter(r) - >>> next(r_iter) - 0 - >>> [x + 1 for x in r] - [1, 2, 3, 4, 5, 6] - >>> [x + 1 for x in r_iter] - [2, 3, 4, 5, 6] - >>> next(r_iter) - StopIteration - """, - }, - { - 'code': """ - >>> map_iter = map(lambda x : x + 10, range(5)) - >>> next(map_iter) - 10 - >>> next(map_iter) - 11 - >>> list(map_iter) - [12, 13, 14] - >>> for e in filter(lambda x : x % 4 == 0, range(1000, 1008)): - ... print(e) - 1000 - 1004 - >>> [x + y for x, y in zip([1, 2, 3], [4, 5, 6])] - [5, 7, 9] - """, - } - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab05/tests/list-mutation.py b/lab/sol-lab05/tests/list-mutation.py deleted file mode 100644 index 166240bb31..0000000000 --- a/lab/sol-lab05/tests/list-mutation.py +++ /dev/null @@ -1,51 +0,0 @@ -test = { - 'name': 'List Mutation', - 'points': 0, - 'suites': [ - { - 'type': 'wwpp', - 'cases': [ - { - 'code': """ - >>> # If nothing would be output by Python, type Nothing - >>> # If the code would error, type Error - >>> s = [6, 7, 8] - >>> print(s.append(6)) - None - >>> s - [6, 7, 8, 6] - >>> s.insert(0, 9) - >>> s - [9, 6, 7, 8, 6] - >>> x = s.pop(1) - >>> s - [9, 7, 8, 6] - >>> s.remove(x) - >>> s - [9, 7, 8] - >>> a, b = s, s[:] - >>> a is s - True - >>> b == s - True - >>> b is s - False - >>> a.pop() - 8 - >>> a + b - [9, 7, 9, 7, 8] - >>> s = [3] - >>> s.extend([4, 5]) - >>> s - [3, 4, 5] - >>> a - [9, 7] - >>> s.extend([s.append(9), s.append(10)]) - >>> s - [3, 4, 5, 9, 10, None, None] - """ - } - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab05/tree.py b/lab/sol-lab05/tree.py deleted file mode 100644 index 15c2d26187..0000000000 --- a/lab/sol-lab05/tree.py +++ /dev/null @@ -1,65 +0,0 @@ - -# 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)]) \ No newline at end of file diff --git a/lab/sol-lab06/cardgame.py b/lab/sol-lab06/cardgame.py deleted file mode 100644 index e3eb6d1610..0000000000 --- a/lab/sol-lab06/cardgame.py +++ /dev/null @@ -1,134 +0,0 @@ -from classes import * -from cards import * - -try: - import readline -except ImportError: - pass - -########### -# Parsing # -########### - -def card_parse(line, handsize): - tokens = line.split() - if not tokens: - raise SyntaxError('No command given') - elif len(tokens) > 1: - raise SyntaxError('Too many inputs') - card_index = tokens.pop(0) - if not card_index.isdigit(): - raise SyntaxError('Wrong type of input') - card_index = int(card_index) - if card_index >= handsize or card_index < 0: - raise SyntaxError('Invalid card number') - return card_index - -def name_parse(line): - if not line: - raise SyntaxError('No command given') - return line - -######## -# REPL # -######## - -def read_eval_print_loop(): - while True: - try: - line = input('What is your name?> ') - name = name_parse(line) - break - except (KeyboardInterrupt, EOFError, SystemExit): # If you ctrl-c or ctrl-d - print('\nSee you next game!') - return - except SyntaxError as e: - print('ERROR:', e) - p1 = Player(player_deck, name) - p2 = Player(opponent_deck, 'Opponent') - print(WELCOME_MESSAGE) - duel = Game(p1, p2) - draw = True - while True: - if duel.game_won() == 1: - print(WIN_MESSAGE) - return - elif duel.game_won() == 2: - print(LOSE_MESSAGE) - return - print() - try: - if draw: - p1.draw() - p2.draw() - else: - draw = True - p1.display_hand() - print('Please enter the number next to the card you would like to play this round.') - line = input('card> ') - card_index = card_parse(line, len(p1.hand)) - duel.play_round(p1.play(card_index), p2.play_random()) - duel.display_scores() - except (KeyboardInterrupt, EOFError, SystemExit): # If you ctrl-c or ctrl-d - print('\nGood game. Bye!') - return - except AssertionError: # Deck out - if p1.deck.is_empty() and p2.deck.is_empty(): - print(TIE_MESSAGE) - return - elif p1.deck.is_empty(): - print(PLAYER_DECKOUT_MESSAGE) - return - else: - print(OPPONENT_DECKOUT_MESSAGE) - return - except SyntaxError as e: - print('ERROR:', e) - draw = False - -################# -# Configuration # -################# - -WELCOME_MESSAGE = """ -Welcome to Magic: The Lambda-ing! - -Your code has taken on a mind of its own and has -challenged you to a game of cards! If you need a refresher -on the rules, check out the section on the project page. - -Let's get this duel started, shall we? -""" - -WIN_MESSAGE = """ -You have vanquished your foe in a duel! - -Congratulations! You won this game of Magic: The Lambda-ing! -""" - -LOSE_MESSAGE = """ -You have been defeated by your foe in a duel! - -I'm sorry, you lost this game of Magic: The Lambda-ing. -""" - -TIE_MESSAGE = """ -You and your opponent have no cards left in your decks! - -You tied this game of Magic: The Lambda-ing. Who will win if you play again? -""" - -PLAYER_DECKOUT_MESSAGE = """ -You have no cards left in your deck! - -I'm sorry, you lost this game of Magic: The Lambda-ing. -""" - -OPPONENT_DECKOUT_MESSAGE = """ -Your opponent has no cards left in their deck! - -Congratulations! You won this game of Magic: The Lambda-ing! -""" - -if __name__ == '__main__': - read_eval_print_loop() \ No newline at end of file diff --git a/lab/sol-lab06/cards.py b/lab/sol-lab06/cards.py deleted file mode 100644 index d4563c43f8..0000000000 --- a/lab/sol-lab06/cards.py +++ /dev/null @@ -1,42 +0,0 @@ -from classes import * - -standard_cards = [ - TACard('Rachel, Bringer of Boba', 1200, 2200), - TutorCard('RichRocks', 1900, 1500), - TutorCard('Apoλλo, Answerer of Ed', 2200, 1100), - TutorCard('Ratthew, King of the Rats', 2000, 1000), - TutorCard('Hans, Compiler of Chaos', 2200, 1000), - TACard('Cyrus, Cereal Consumer', 1500, 1500), - TutorCard('jade, jabbering jester', 2299, 1001), - TutorCard('Arnold, Description Here', 1600, 1500), - TutorCard('Sophia, Soda Slayer', 1100, 2000), - AICard('El Shroomster the Lamb, duh!', 1000, 2300), - TACard('Abby, Queen of Geese', 1500, 1800), - TutorCard('The λce', 1000, 2000), - AICard('Amber, Aetherweaver', 1700, 1700), - TACard('The Ace', 1600, 1800), - AICard('Bashwat, Brains of the Balawat', 1700, 1600), - AICard('Henry, Huggable Hipster', 1499, 1501), - AICard('The Smasher', 2200, 1200), - TutorCard('Ronald, Recursor of Recursion', 1000, 2300), - TutorCard('Albs, Ahri Ace', 1700, 1100), - TACard('Kevin, Random Schmoe Who Is Incredibly Powerful For Some Reason', 1000, 2200), - TACard('λaryn, λord of λambdas', 1700, 1600), - AICard('Alexander the Not So Great', 1700, 1700), - AICard('λnto\'s λI ', 2000, 1000), - TACard('Kotryna, Chaotic Neutral', 1500, 1600), - TutorCard('Darlnim, Darklord', 1500, 1500), - AICard('Jade, Lucky Fool', 1500, 1600), - AICard('Anshu, Chieftain of Recursion', 1800, 1500), - AICard('Rudy, Squirrel Whisperer', 1800, 1500), - TACard('Christina, Stick Breaker', 1900, 1100), - AICard('Adhvaith Thurvas, Caffeine-Powered Adverserial AI', 2100, 1000), - TACard('Bryce, Fuzzy Fire Flinger', 1100, 2300), - AICard('Zattack', 2300, 1000), - InstructorCard('DeNero, Antihero', 6650, 6650), - InstructorCard('Rao, Chaotic Wanderer', 0, 10000) -] - -standard_deck = Deck(standard_cards) -player_deck = standard_deck.copy() -opponent_deck = standard_deck.copy() \ No newline at end of file diff --git a/lab/sol-lab06/classes.py b/lab/sol-lab06/classes.py deleted file mode 100644 index 3684520896..0000000000 --- a/lab/sol-lab06/classes.py +++ /dev/null @@ -1,383 +0,0 @@ -# Magic the Lambda-ing! - -import random - -class Card: - cardtype = 'Staff' - - def __init__(self, name, attack, defense): - """ - Create a Card object with a name, attack, - and defense. - >>> staff_member = Card('staff', 400, 300) - >>> staff_member.name - 'staff' - >>> staff_member.attack - 400 - >>> staff_member.defense - 300 - >>> other_staff = Card('other', 300, 500) - >>> other_staff.attack - 300 - >>> other_staff.defense - 500 - """ - self.name = name - self.attack = attack - self.defense = defense - - def power(self, opponent_card): - """ - Calculate power as: - (player card's attack) - (opponent card's defense) - >>> staff_member = Card('staff', 400, 300) - >>> other_staff = Card('other', 300, 500) - >>> staff_member.power(other_staff) - -100 - >>> other_staff.power(staff_member) - 0 - >>> third_card = Card('third', 200, 400) - >>> staff_member.power(third_card) - 0 - >>> third_card.power(staff_member) - -100 - """ - return self.attack - opponent_card.defense - - - def effect(self, opponent_card, player, opponent): - """ - Cards have no default effect. - """ - return - - def __repr__(self): - """ - Returns a string which is a readable version of - a card, in the form: - : , [, ] - """ - return '{}: {}, [{}, {}]'.format(self.name, self.cardtype, self.attack, self.defense) - - def copy(self): - """ - Returns a copy of this card. - """ - return Card(self.name, self.attack, self.defense) - -class Player: - def __init__(self, deck, name): - """Initialize a Player object. - A Player starts the game by drawing 5 cards from their deck. Each turn, - a Player draws another card from the deck and chooses one to play. - >>> test_card = Card('test', 100, 100) - >>> test_deck = Deck([test_card.copy() for _ in range(6)]) - >>> test_player = Player(test_deck, 'tester') - >>> len(test_deck.cards) - 1 - >>> len(test_player.hand) - 5 - """ - self.deck = deck - self.name = name - self.hand = [deck.draw() for _ in range(5)] - - def draw(self): - """Draw a card from the player's deck and add it to their hand. - >>> test_card = Card('test', 100, 100) - >>> test_deck = Deck([test_card.copy() for _ in range(6)]) - >>> test_player = Player(test_deck, 'tester') - >>> test_player.draw() - >>> len(test_deck.cards) - 0 - >>> len(test_player.hand) - 6 - """ - assert not self.deck.is_empty(), 'Deck is empty!' - self.hand.append(self.deck.draw()) - - def play(self, index): - """Remove and return a card from the player's hand at the given INDEX. - >>> from cards import * - >>> test_player = Player(standard_deck, 'tester') - >>> ta1, ta2 = TACard("ta_1", 300, 400), TACard("ta_2", 500, 600) - >>> tutor1, tutor2 = TutorCard("t1", 200, 500), TutorCard("t2", 600, 400) - >>> test_player.hand = [ta1, ta2, tutor1, tutor2] - >>> test_player.play(0) is ta1 - True - >>> test_player.play(2) is tutor2 - True - >>> len(test_player.hand) - 2 - """ - return self.hand.pop(index) - - - def display_hand(self): - """ - Display the player's current hand to the user. - """ - print('Your hand:') - for card_index, displayed_card in zip(range(len(self.hand)),[str(card) for card in self.hand]): - indent = ' '*(5 - len(str(card_index))) - print(card_index, indent + displayed_card) - - def play_random(self): - """ - Play a random card from hand. - """ - return self.play(random.randrange(len(self.hand))) - -###################### -# Optional Questions # -###################### - -class AICard(Card): - cardtype = 'AI' - - def effect(self, opponent_card, player, opponent): - """ - Add the top two cards of your deck to your hand via drawing. - Once you have finished writing your code for this problem, - set implemented to True so that the text is printed when - playing an AICard. - - >>> from cards import * - >>> player1, player2 = Player(standard_deck.copy(), 'p1'), Player(standard_deck.copy(), 'p2') - >>> opponent_card = Card("other", 500, 500) - >>> test_card = AICard("AI Card", 500, 500) - >>> initial_deck_length = len(player1.deck.cards) - >>> initial_hand_size = len(player1.hand) - >>> test_card.effect(opponent_card, player1, player2) - AI Card allows me to draw two cards! - >>> initial_hand_size == len(player1.hand) - 2 - True - >>> initial_deck_length == len(player1.deck.cards) + 2 - True - """ - implemented = False - player.draw() - player.draw() - implemented = True - # You should add your implementation above this. - if implemented: - print(f"{self.name} allows me to draw two cards!") - - def copy(self): - """ - Create a copy of this card. - """ - return AICard(self.name, self.attack, self.defense) - -class TutorCard(Card): - cardtype = 'Tutor' - - def effect(self, opponent_card, player, opponent): - """ - Add a copy of the first card in your hand - to your hand, at the cost of losing the current - round. If there are no cards in hand, this card does - not add any cards, but still loses the round. To - implement the second part of this effect, a Tutor - card's power should be less than all non-Tutor cards. - - >>> from cards import * - >>> player1, player2 = Player(standard_deck.copy(), 'p1'), Player(standard_deck.copy(), 'p2') - >>> opponent_card = Card("other", 500, 500) - >>> test_card = TutorCard("Tutor Card", 10000, 10000) - >>> player1.hand = [Card("card1", 0, 100), Card("card2", 100, 0)] - >>> test_card.effect(opponent_card, player1, player2) - Tutor Card allows me to add a copy of a card to my hand! - >>> print(player1.hand) - [card1: Staff, [0, 100], card2: Staff, [100, 0], card1: Staff, [0, 100]] - >>> player1.hand[0] is player1.hand[2] # must add a copy! - False - >>> player1.hand = [] - >>> test_card.effect(opponent_card, player1, player2) - >>> print(player1.hand) # must not add a card if not available - [] - >>> test_card.power(opponent_card) < opponent_card.power(test_card) - True - """ - added = False - if len(player.hand) > 0: - player.hand += [player.hand[0].copy()] - added = True - # You should add your implementation above this. - if added: - print(f"{self.name} allows me to add a copy of a card to my hand!") - - def power(self, opponent_card): - return -float("inf") - - - def copy(self): - """ - Create a copy of this card. - """ - return TutorCard(self.name, self.attack, self.defense) - -class TACard(Card): - cardtype = 'TA' - - def effect(self, opponent_card, player, opponent, arg=None): - """ - Discard the card with the highest `power` in your hand, - and add the discarded card's attack and defense - to this card's own respective stats. - - >>> from cards import * - >>> player1, player2 = Player(standard_deck.copy(), 'p1'), Player(standard_deck.copy(), 'p2') - >>> opponent_card = Card("other", 500, 500) - >>> test_card = TACard("TA Card", 500, 500) - >>> player1.hand = [] - >>> test_card.effect(opponent_card, player1, player2) # if no cards in hand, no effect. - >>> print(test_card.attack, test_card.defense) - 500 500 - >>> player1.hand = [Card("card1", 0, 100), TutorCard("tutor", 10000, 10000), Card("card3", 100, 0)] - >>> test_card.effect(opponent_card, player1, player2) # must use card's power method. - TA Card discards card3 from my hand to increase its own power! - >>> print(player1.hand) - [card1: Staff, [0, 100], tutor: Tutor, [10000, 10000]] - >>> print(test_card.attack, test_card.defense) - 600 500 - """ - best_card = None - if len(player.hand) > 0: - best_card = max(player.hand, key = lambda c: c.power(opponent_card)) - self.attack += best_card.attack - self.defense += best_card.defense - player.hand.remove(best_card) - # You should add your implementation above this. - if best_card: - print(f"{self.name} discards {best_card.name} from my hand to increase its own power!") - - - def copy(self): - """ - Create a copy of this card. - """ - return TACard(self.name, self.attack, self.defense) - -class InstructorCard(Card): - cardtype = 'Instructor' - - def effect(self, opponent_card, player, opponent, arg=None): - """ - Survives multiple rounds, as long as it has a non-negative - attack or defense at the end of a round. At the beginning of the round, - its attack and defense are permanently reduced by 1000 each. - If this card would survive, it is added back to the hand. - - >>> from cards import * - >>> player1, player2 = Player(standard_deck.copy(), 'p1'), Player(standard_deck.copy(), 'p2') - >>> opponent_card = Card("other", 500, 500) - >>> test_card = InstructorCard("Instructor Card", 1000, 1000) - >>> player1.hand = [Card("card1", 0, 100)] - >>> test_card.effect(opponent_card, player1, player2) - Instructor Card returns to my hand! - >>> print(player1.hand) # survives with non-negative attack - [card1: Staff, [0, 100], Instructor Card: Instructor, [0, 0]] - >>> player1.hand = [Card("card1", 0, 100)] - >>> test_card.effect(opponent_card, player1, player2) - >>> print(player1.hand) - [card1: Staff, [0, 100]] - >>> print(test_card.attack, test_card.defense) - -1000 -1000 - """ - re_add = False - self.attack -= 1000 - self.defense -= 1000 - if max(self.attack, self.defense) >= 0: - re_add = True - player.hand += [self] - # You should add your implementation above this. - if re_add: - print(f"{self.name} returns to my hand!") - - def copy(self): - return InstructorCard(self.name, self.attack, self.defense) - - -######################################## -# Do not edit anything below this line # -######################################## - -class Deck: - def __init__(self, cards): - """ - With a list of cards as input, create a deck. - This deck should keep track of the cards it contains, and - we should be able to draw from the deck, taking a random - card out of it. - """ - self.cards = cards - - def draw(self): - """ - Pick a random card from the deck and remove it from the deck. - """ - assert self.cards, 'The deck is empty!' - rand_index = random.randrange(len(self.cards)) - return self.cards.pop(rand_index) - - def is_empty(self): - return len(self.cards) == 0 - - def copy(self): - """ - Create a copy of this deck. - """ - return Deck([card.copy() for card in self.cards]) - -class Game: - win_score = 8 - - def __init__(self, player1, player2): - """ - Initialize a game of Magic: the Lambda-ing. - """ - self.player1, self.player2 = player1, player2 - self.p1_score = 0 - self.p2_score = 0 - - def play_round(self, p1_card, p2_card): - """ - After each player picks a card, play them against - each other. - """ - p1_card.effect(p2_card, self.player1, self.player2) - p2_card.effect(p1_card, self.player2, self.player1) - p1_power = p1_card.power(p2_card) - p2_power = p2_card.power(p1_card) - if p1_power > p2_power: - # Player 1 wins the round. - self.p1_score += 1 - result = 'won' - elif p2_power > p1_power: - # Player 2 wins the round. - self.p2_score += 1 - result = 'lost' - else: - # This round is a draw. - result = 'tied' - # Display results to user. - print('You {} this round!'.format(result)) - print('{}\'s card: {}; Power: {}'.format(self.player1.name, p1_card, p1_power)) - print('Opponent\'s card: {}; Power: {}'.format(p2_card, p2_power)) - - def game_won(self): - """ - Check if the game is won and, if so, - which player won. - """ - if self.p1_score < self.win_score and self.p2_score < self.win_score: - return 0 - return 1 if self.p1_score > self.p2_score else 2 - - def display_scores(self): - """ - Display players' scores to the user. - """ - print('{}\'s score: {}'.format(self.player1.name, self.p1_score)) - print('Opponent\'s score: {}'.format(self.p2_score)) \ No newline at end of file diff --git a/lab/sol-lab06/construct_check.py b/lab/sol-lab06/construct_check.py deleted file mode 100644 index bee096e7e4..0000000000 --- a/lab/sol-lab06/construct_check.py +++ /dev/null @@ -1,180 +0,0 @@ -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/lab/sol-lab06/index.html b/lab/sol-lab06/index.html deleted file mode 100644 index 767e545ba4..0000000000 --- a/lab/sol-lab06/index.html +++ /dev/null @@ -1,854 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Lab 6 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Lab 6 Solutions - - - - - - -

-
- -

Solution Files

- - - -

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.

- - -

YouTube link

-
- - -

Object-Oriented Programming

- - -

Here's a refresher on Object-Oriented Programming. It's okay to skip directly to -the questions and refer back here if you get stuck.

- - - -
- -

Object-oriented programming (OOP) uses objects and classes to organize programs. Here's an example of a class:

- -
class Car:
-    max_tires = 4
-
-    def __init__(self, color):
-        self.tires = Car.max_tires
-        self.color = color
-
-    def drive(self):
-        if self.tires < Car.max_tires:
-            return self.color + ' car cannot drive!'
-        return self.color + ' car goes vroom!'
-
-    def pop_tire(self):
-        if self.tires > 0:
-            self.tires -= 1
- -

Class: The type of an object. The Car class (shown above) describes the characteristics of all Car - objects.

- -

Object: A single instance of a class. In Python, a new object is created by - calling a class.

- -
>>> ferrari = Car('red')
- -

Here, ferrari is a name bound to a Car object.

- -

Class attribute: A variable that belongs to a class and is accessed via dot notation. The Car class has a max_tires attribute.

- -
>>> Car.max_tires
-4
- -

Instance attribute: A variable that belongs to a particular object. Each Car object has a tires attribute and a color attribute. Like class attributes, instance attributes are accessed via dot notation.

- -
>>> ferrari.color
-'red'
->>> ferrari.tires
-4
->>> ferrari.color = 'green'
->>> ferrari.color
-'green'
- -

Method: A function that belongs to an object and is called via dot notation. By convention, the first parameter of a method is self.

- -

When one of an object's methods is called, the object is implicitly provided as the argument for self. For example, the drive method of the ferrari object is called with empty parentheses because self is implicitly bound to the ferrari object.

- -
>>> ferrari = Car('red')
->>> ferrari.drive()
-'red car goes vroom!'
- -

We can also call the original Car.drive function. The original function does not belong to any particular Car object, so we must provide an explicit argument for self.

- -
>>> ferrari = Car('red')
->>> Car.drive(ferrari)
-'red car goes vroom!'
- -

__init__: A special function that is called automatically when a new instance of a class is created.

- -

Notice how the drive method takes in self as an argument, but it - looks like we didn't pass one in! This is because the dot notation - implicitly passes in ferrari as self for us. So in this example, self is bound to the - object called ferrari in the global frame.

- -

To evaluate the expression Car('red'), Python creates a new Car object. Then, Python calls the __init__ function of the Car class with self bound to the new object and color bound to 'red'.

- -
- - -

Q1: Bank Account

- - -

Extend the BankAccount class to include a transactions attribute. This attribute should be a list that keeps track of each transaction made on the account. Whenever the deposit or withdraw method is called, a new Transaction instance should be created and added to the list, regardless of whether that action is successful.

- -

The Transaction class should have the following attributes:

- -
    -
  • before: The account balance before the transaction.
  • -
  • after: The account balance after the transaction.
  • -
  • id: The transaction ID, which is the number of previous transactions (deposits or withdrawals) made on that account. This id should be unique within the scope of each account, but not necessarily unique across all accounts.
  • -
- -

In addition, the Transaction class should have the following methods:

- -
    -
  • changed(): Returns True if the balance changed (i.e., before is different from after), otherwise returns False.
  • -
  • report(): Returns a string describing the transaction. The string should start with the transaction ID and describe the change in balance. Take a look at the doctests for the expected output.
  • -
- -

NOTE. -The BankAccount class is the same as the original Account class, meaning that it is exactly like the Account class seen under the Class Practice section excluding the changes made in Question #2 (Retirement) and #3 (FreeChecking). We just had to have a different class name for grading purposes.

- - - -
class Transaction:
-    def __init__(self, id, before, after):
-        self.id = id
-        self.before = before
-        self.after = after
-
-    def changed(self):
-        """Return whether the transaction resulted in a changed balance."""
-
return self.before != self.after
- def report(self): - """Return a string describing the transaction. - - >>> Transaction(3, 20, 10).report() - '3: decreased 20->10' - >>> Transaction(4, 20, 50).report() - '4: increased 20->50' - >>> Transaction(5, 50, 50).report() - '5: no change' - """ - msg = 'no change' - if self.changed(): -
if self.after < self.before: - verb = 'decreased' - else: - verb = 'increased' - msg = verb + ' ' + str(self.before) + '->' + str(self.after)
return str(self.id) + ': ' + msg - -class BankAccount: - """A bank account that tracks its transaction history. - - >>> a = BankAccount('Eric') - >>> a.deposit(100) # Transaction 0 for a - 100 - >>> b = BankAccount('Erica') - >>> a.withdraw(30) # Transaction 1 for a - 70 - >>> a.deposit(10) # Transaction 2 for a - 80 - >>> b.deposit(50) # Transaction 0 for b - 50 - >>> b.withdraw(10) # Transaction 1 for b - 40 - >>> a.withdraw(100) # Transaction 3 for a - 'Insufficient funds' - >>> len(a.transactions) - 4 - >>> len([t for t in a.transactions if t.changed()]) - 3 - >>> for t in a.transactions: - ... print(t.report()) - 0: increased 0->100 - 1: decreased 100->70 - 2: increased 70->80 - 3: no change - >>> b.withdraw(100) # Transaction 2 for b - 'Insufficient funds' - >>> b.withdraw(30) # Transaction 3 for b - 10 - >>> for t in b.transactions: - ... print(t.report()) - 0: increased 0->50 - 1: decreased 50->40 - 2: no change - 3: decreased 40->10 - """ - - # *** YOU NEED TO MAKE CHANGES IN SEVERAL PLACES IN THIS CLASS *** -
def next_id(self): - # There are many ways to implement this counter, such as using an instance - # attribute to track the next ID. - return len(self.transactions)
- def __init__(self, account_holder): - self.balance = 0 - self.holder = account_holder -
self.transactions = []
- def deposit(self, amount): - """Increase the account balance by amount, add the deposit - to the transaction history, and return the new balance. - """ -
self.transactions.append(Transaction(self.next_id(), self.balance, self.balance + amount))
self.balance = self.balance + amount - return self.balance - - def withdraw(self, amount): - """Decrease the account balance by amount, add the withdraw - to the transaction history, and return the new balance. - """ - if amount > self.balance: -
self.transactions.append(Transaction(self.next_id(), self.balance, self.balance))
return 'Insufficient funds' -
self.transactions.append(Transaction(self.next_id(), self.balance, self.balance - amount))
self.balance = self.balance - amount - return self.balance
- -
- -

Use Ok to test your code:

python3 ok -q BankAccount
- -
- - -

Q2: Email

- - -

An email system has three classes: Email, Server, and Client. A Client can -compose an email, which it will send to the Server. The Server then delivers it to the -inbox of another Client. To achieve this, a Server has a dictionary called -clients that maps the name of the Client to the Client instance.

- -

Assume that a client never changes the server that it uses, and it only composes -emails using that server.

- -

Fill in the definitions below to finish the implementation! The Email class -has been completed for you.

- -

Important: Before you start, make sure you read the entire code snippet to understand the relationships between the classes, and pay attention to the parameter type of the methods. Think about what varible do you have access to in each method and how can you use that to access the other classes and their methods.

- -

Note: The sender parameter from the __init__(self, msg, sender, recipient_name) method in the Email class is a Client instance.

- -

Note: The client parameter from the register_client(self, client) method in the Server class is a Client instance.

- -

Note: The email parameter from the send(self, email) method in the Server class is an Email instance.

- - - -
class Email:
-    """An email has the following instance attributes:
-
-        msg (str): the contents of the message
-        sender (Client): the client that sent the email
-        recipient_name (str): the name of the recipient (another client)
-    """
-    def __init__(self, msg, sender, recipient_name):
-        self.msg = msg
-        self.sender = sender
-        self.recipient_name = recipient_name
-
-class Server:
-    """Each Server has one instance attribute called clients that is a
-    dictionary from client names to client objects.
-    """
-    def __init__(self):
-        self.clients = {}
-
-    def send(self, email):
-        """Append the email to the inbox of the client it is addressed to.
-            email is an instance of the Email class.
-        """
-
self.clients[email.recipient_name].inbox.append(email)
- def register_client(self, client): - """Add a client to the clients mapping (which is a - dictionary from client names to client instances). - client is an instance of the Client class. - """ -
self.clients[client.name] = client
-class Client: - """A client has a server, a name (str), and an inbox (list). - - >>> s = Server() - >>> a = Client(s, 'Alice') - >>> b = Client(s, 'Bob') - >>> a.compose('Hello, World!', 'Bob') - >>> b.inbox[0].msg - 'Hello, World!' - >>> a.compose('CS 61A Rocks!', 'Bob') - >>> len(b.inbox) - 2 - >>> b.inbox[1].msg - 'CS 61A Rocks!' - >>> b.inbox[1].sender.name - 'Alice' - """ - def __init__(self, server, name): - self.inbox = [] - self.server = server - self.name = name -
server.register_client(self)
- def compose(self, message, recipient_name): - """Send an email with the given message to the recipient.""" -
email = Email(message, self, recipient_name)
self.server.send(email)
- -
- -

Use Ok to test your code:

python3 ok -q Client
- -
- - -

Q3: Make Change

- - -

Implement make_change, which takes a positive integer amount and a -dictionary of coins. The coins dictionary keys are positive integer -denominations and its values are positive integer coin counts. For example, -{1: 4, 5: 2} represents four pennies and two nickels. The make_change -function returns a list of coins that sum to amount, where the count of -any denomination k in the return value is at most coins[k].

- -

If there are multiple ways to make change for amount, prefer to use as many -of the smallest coins available and place the smallest coins first in the -returned list.

- - - -
def make_change(amount, coins):
-    """Return a list of coins that sum to amount, preferring the smallest coins
-    available and placing the smallest coins first in the returned list.
-
-    The coins argument is a dictionary with keys that are positive integer
-    denominations and values that are positive integer coin counts.
-
-    >>> make_change(2, {2: 1})
-    [2]
-    >>> make_change(2, {1: 2, 2: 1})
-    [1, 1]
-    >>> make_change(4, {1: 2, 2: 1})
-    [1, 1, 2]
-    >>> make_change(4, {2: 1}) == None
-    True
-
-    >>> coins = {2: 2, 3: 2, 4: 3, 5: 1}
-    >>> make_change(4, coins)
-    [2, 2]
-    >>> make_change(8, coins)
-    [2, 2, 4]
-    >>> make_change(25, coins)
-    [2, 3, 3, 4, 4, 4, 5]
-    >>> coins[8] = 1
-    >>> make_change(25, coins)
-    [2, 2, 4, 4, 5, 8]
-    """
-    if not coins:
-        return None
-    smallest = min(coins)
-    rest = remove_one(coins, smallest)
-    if amount < smallest:
-        return None
-
elif amount == smallest: - return [smallest] - else: - result = make_change(amount-smallest, rest) - if result: - return [smallest] + result - else: - return make_change(amount, rest)
- -
- -

You should use the remove_one function in your implementation:

- - - -
def remove_one(coins, coin):
-    """Remove one coin from a dictionary of coins. Return a new dictionary,
-    leaving the original dictionary coins unchanged.
-
-    >>> coins = {2: 5, 3: 2, 6: 1}
-    >>> remove_one(coins, 2) == {2: 4, 3: 2, 6: 1}
-    True
-    >>> remove_one(coins, 6) == {2: 5, 3: 2}
-    True
-    >>> coins == {2: 5, 3: 2, 6: 1} # Unchanged
-    True
-    """
-    copy = dict(coins)
-    count = copy.pop(coin) - 1  # The coin denomination is removed
-    if count:
-        copy[coin] = count      # The coin denomination is added back
-    return copy
- -
- -

Hint: Try using the smallest coin to make change. If it turns out that there -is no way to make change using the smallest coin, then try making change -without the smallest coin.

- -

Hint: The simplest solution does not involve defining any local functions, -but you can define additional functions if you wish.

- -

Definitely try to solve this without reading the walkthrough, but if you're -really stuck then read the walkthrough.

- - - -
-The code for make_change(amount, coins) should do the following: - -
    -
  1. Check if amount == smallest, in which case return a one-element list containing just smallest.
  2. -
  3. Otherwise, call make_change(amount-smallest, rest), which returns either None or a list of numbers.
  4. -
  5. If the call in Step 2 returned a list, then return a longer list that includes smallest at the front.
  6. -
  7. If the call in Step 2 returned None, then there's no way to use the smallest coin, so just make_change(amount, rest) -
- - -

Use Ok to test your code:

python3 ok -q make_change
- -
- - -

Q4: Change Machine

- - -

Complete the change method of the ChangeMachine class. A ChangeMachine -instance holds some coins, which are initially all pennies. The change -method takes a positive integer coin, adds that coin to its coins, and then -returns a list that sums to coin. The machine prefers to return as many of -the smallest coins available, ordered from smallest to largest. The coins -returned by change are removed from the machine's coins.

- - - -
class ChangeMachine:
-    """A change machine holds a certain number of coins, initially all pennies.
-    The change method adds a single coin of some denomination X and returns a
-    list of coins that sums to X. The machine prefers to return the smallest
-    coins available. The total value in the machine never changes, and it can
-    always make change for any coin (perhaps by returning the coin passed in).
-
-    The coins attribute is a dictionary with keys that are positive integer
-    denominations and values that are positive integer coin counts.
-
-    >>> m = ChangeMachine(2)
-    >>> m.coins == {1: 2}
-    True
-    >>> m.change(2)
-    [1, 1]
-    >>> m.coins == {2: 1}
-    True
-    >>> m.change(2)
-    [2]
-    >>> m.coins == {2: 1}
-    True
-    >>> m.change(3)
-    [3]
-    >>> m.coins == {2: 1}
-    True
-
-    >>> m = ChangeMachine(10) # 10 pennies
-    >>> m.coins == {1: 10}
-    True
-    >>> m.change(5) # takes a nickel & returns 5 pennies
-    [1, 1, 1, 1, 1]
-    >>> m.coins == {1: 5, 5: 1} # 5 pennies & a nickel remain
-    True
-    >>> m.change(3)
-    [1, 1, 1]
-    >>> m.coins == {1: 2, 3: 1, 5: 1}
-    True
-    >>> m.change(2)
-    [1, 1]
-    >>> m.change(2) # not enough 1's remaining; return a 2
-    [2]
-    >>> m.coins == {2: 1, 3: 1, 5: 1}
-    True
-    >>> m.change(8) # cannot use the 2 to make 8, so use 3 & 5
-    [3, 5]
-    >>> m.coins == {2: 1, 8: 1}
-    True
-    >>> m.change(1) # return the penny passed in (it's the smallest)
-    [1]
-    >>> m.change(9) # return the 9 passed in (no change possible)
-    [9]
-    >>> m.coins == {2: 1, 8: 1}
-    True
-    >>> m.change(10)
-    [2, 8]
-    >>> m.coins == {10: 1}
-    True
-
-    >>> m = ChangeMachine(9)
-    >>> [m.change(k) for k in [2, 2, 3]]
-    [[1, 1], [1, 1], [1, 1, 1]]
-    >>> m.coins == {1: 2, 2: 2, 3: 1}
-    True
-    >>> m.change(5) # Prefers [1, 1, 3] to [1, 2, 2] (more pennies)
-    [1, 1, 3]
-    >>> m.change(7)
-    [2, 5]
-    >>> m.coins == {2: 1, 7: 1}
-    True
-    """
-    def __init__(self, pennies):
-        self.coins = {1: pennies}
-
-    def change(self, coin):
-        """Return change for coin, removing the result from self.coins."""
-
self.coins[coin] = 1 + self.coins.get(coin, 0) # Put the coin in the machine - result = make_change(coin, self.coins) - for c in result: - self.coins = remove_one(self.coins, c) - return result
- -
- -

Hint: Call the make_change function in order to compute the result of -change, but update self.coins before returning that result.

- -

Definitely try to solve this without reading the walkthrough, but if you're -really stuck then read the walkthrough.

- - - -
-The code for change(self, coin) should do the following: - -
    -
  1. Add the coin to the machine. This way, you can just make change and you'll always get some result, although it might just be that coin back. The simplest way is to get the count of that coin (defaulting to 0) and add 1 to it: self.coins[coin] = 1 + self.coins.get(coin, 0)
  2. -
  3. Call make_change(coin, self.coins) and assign the result to a name (such as result). You'll return this at the end.
  4. -
  5. Before returning, reduce the count of each coin you are returning. One way is to repeatedly call remove_one(self.coins, c) for each coin c in the result of calling make_change in Step 2.
  6. -
  7. Return the result of calling make_change in Step 2. -
- - -

Use Ok to test your code:

python3 ok -q ChangeMachine
- -
- - -

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.

- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/lab/sol-lab06/lab06.ok b/lab/sol-lab06/lab06.ok deleted file mode 100644 index becd1ecb3f..0000000000 --- a/lab/sol-lab06/lab06.ok +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "Lab 6", - "endpoint": "cal/cs61a/fa24/lab06", - "src": [ - "lab06.py", - "classes.py" - ], - "tests": { - "lab*.py": "doctest", - "tests/*.py": "ok_test", - "classes.py": "doctest" - }, - "default_tests": [ - "BankAccount", - "Client", - "make_change", - "ChangeMachine" - ], - "protocols": [ - "restore", - "file_contents", - "analytics", - "unlock", - "grading", - "backup" - ] -} \ No newline at end of file diff --git a/lab/sol-lab06/lab06.py b/lab/sol-lab06/lab06.py deleted file mode 100644 index b239b9ef83..0000000000 --- a/lab/sol-lab06/lab06.py +++ /dev/null @@ -1,303 +0,0 @@ -class Transaction: - def __init__(self, id, before, after): - self.id = id - self.before = before - self.after = after - - def changed(self): - """Return whether the transaction resulted in a changed balance.""" - return self.before != self.after - - def report(self): - """Return a string describing the transaction. - - >>> Transaction(3, 20, 10).report() - '3: decreased 20->10' - >>> Transaction(4, 20, 50).report() - '4: increased 20->50' - >>> Transaction(5, 50, 50).report() - '5: no change' - """ - msg = 'no change' - if self.changed(): - if self.after < self.before: - verb = 'decreased' - else: - verb = 'increased' - msg = verb + ' ' + str(self.before) + '->' + str(self.after) - return str(self.id) + ': ' + msg - -class BankAccount: - """A bank account that tracks its transaction history. - - >>> a = BankAccount('Eric') - >>> a.deposit(100) # Transaction 0 for a - 100 - >>> b = BankAccount('Erica') - >>> a.withdraw(30) # Transaction 1 for a - 70 - >>> a.deposit(10) # Transaction 2 for a - 80 - >>> b.deposit(50) # Transaction 0 for b - 50 - >>> b.withdraw(10) # Transaction 1 for b - 40 - >>> a.withdraw(100) # Transaction 3 for a - 'Insufficient funds' - >>> len(a.transactions) - 4 - >>> len([t for t in a.transactions if t.changed()]) - 3 - >>> for t in a.transactions: - ... print(t.report()) - 0: increased 0->100 - 1: decreased 100->70 - 2: increased 70->80 - 3: no change - >>> b.withdraw(100) # Transaction 2 for b - 'Insufficient funds' - >>> b.withdraw(30) # Transaction 3 for b - 10 - >>> for t in b.transactions: - ... print(t.report()) - 0: increased 0->50 - 1: decreased 50->40 - 2: no change - 3: decreased 40->10 - """ - - # *** YOU NEED TO MAKE CHANGES IN SEVERAL PLACES IN THIS CLASS *** - def next_id(self): - # There are many ways to implement this counter, such as using an instance - # attribute to track the next ID. - return len(self.transactions) - - def __init__(self, account_holder): - self.balance = 0 - self.holder = account_holder - self.transactions = [] - - def deposit(self, amount): - """Increase the account balance by amount, add the deposit - to the transaction history, and return the new balance. - """ - self.transactions.append(Transaction(self.next_id(), self.balance, self.balance + amount)) - self.balance = self.balance + amount - return self.balance - - def withdraw(self, amount): - """Decrease the account balance by amount, add the withdraw - to the transaction history, and return the new balance. - """ - if amount > self.balance: - self.transactions.append(Transaction(self.next_id(), self.balance, self.balance)) - return 'Insufficient funds' - self.transactions.append(Transaction(self.next_id(), self.balance, self.balance - amount)) - self.balance = self.balance - amount - return self.balance - - - - -class Email: - """An email has the following instance attributes: - - msg (str): the contents of the message - sender (Client): the client that sent the email - recipient_name (str): the name of the recipient (another client) - """ - def __init__(self, msg, sender, recipient_name): - self.msg = msg - self.sender = sender - self.recipient_name = recipient_name - -class Server: - """Each Server has one instance attribute called clients that is a - dictionary from client names to client objects. - """ - def __init__(self): - self.clients = {} - - def send(self, email): - """Append the email to the inbox of the client it is addressed to. - email is an instance of the Email class. - """ - self.clients[email.recipient_name].inbox.append(email) - - def register_client(self, client): - """Add a client to the clients mapping (which is a - dictionary from client names to client instances). - client is an instance of the Client class. - """ - self.clients[client.name] = client - -class Client: - """A client has a server, a name (str), and an inbox (list). - - >>> s = Server() - >>> a = Client(s, 'Alice') - >>> b = Client(s, 'Bob') - >>> a.compose('Hello, World!', 'Bob') - >>> b.inbox[0].msg - 'Hello, World!' - >>> a.compose('CS 61A Rocks!', 'Bob') - >>> len(b.inbox) - 2 - >>> b.inbox[1].msg - 'CS 61A Rocks!' - >>> b.inbox[1].sender.name - 'Alice' - """ - def __init__(self, server, name): - self.inbox = [] - self.server = server - self.name = name - server.register_client(self) - - def compose(self, message, recipient_name): - """Send an email with the given message to the recipient.""" - email = Email(message, self, recipient_name) - self.server.send(email) - - -def make_change(amount, coins): - """Return a list of coins that sum to amount, preferring the smallest coins - available and placing the smallest coins first in the returned list. - - The coins argument is a dictionary with keys that are positive integer - denominations and values that are positive integer coin counts. - - >>> make_change(2, {2: 1}) - [2] - >>> make_change(2, {1: 2, 2: 1}) - [1, 1] - >>> make_change(4, {1: 2, 2: 1}) - [1, 1, 2] - >>> make_change(4, {2: 1}) == None - True - - >>> coins = {2: 2, 3: 2, 4: 3, 5: 1} - >>> make_change(4, coins) - [2, 2] - >>> make_change(8, coins) - [2, 2, 4] - >>> make_change(25, coins) - [2, 3, 3, 4, 4, 4, 5] - >>> coins[8] = 1 - >>> make_change(25, coins) - [2, 2, 4, 4, 5, 8] - """ - if not coins: - return None - smallest = min(coins) - rest = remove_one(coins, smallest) - if amount < smallest: - return None - elif amount == smallest: - return [smallest] - else: - result = make_change(amount-smallest, rest) - if result: - return [smallest] + result - else: - return make_change(amount, rest) - -def remove_one(coins, coin): - """Remove one coin from a dictionary of coins. Return a new dictionary, - leaving the original dictionary coins unchanged. - - >>> coins = {2: 5, 3: 2, 6: 1} - >>> remove_one(coins, 2) == {2: 4, 3: 2, 6: 1} - True - >>> remove_one(coins, 6) == {2: 5, 3: 2} - True - >>> coins == {2: 5, 3: 2, 6: 1} # Unchanged - True - """ - copy = dict(coins) - count = copy.pop(coin) - 1 # The coin denomination is removed - if count: - copy[coin] = count # The coin denomination is added back - return copy - -class ChangeMachine: - """A change machine holds a certain number of coins, initially all pennies. - The change method adds a single coin of some denomination X and returns a - list of coins that sums to X. The machine prefers to return the smallest - coins available. The total value in the machine never changes, and it can - always make change for any coin (perhaps by returning the coin passed in). - - The coins attribute is a dictionary with keys that are positive integer - denominations and values that are positive integer coin counts. - - >>> m = ChangeMachine(2) - >>> m.coins == {1: 2} - True - >>> m.change(2) - [1, 1] - >>> m.coins == {2: 1} - True - >>> m.change(2) - [2] - >>> m.coins == {2: 1} - True - >>> m.change(3) - [3] - >>> m.coins == {2: 1} - True - - >>> m = ChangeMachine(10) # 10 pennies - >>> m.coins == {1: 10} - True - >>> m.change(5) # takes a nickel & returns 5 pennies - [1, 1, 1, 1, 1] - >>> m.coins == {1: 5, 5: 1} # 5 pennies & a nickel remain - True - >>> m.change(3) - [1, 1, 1] - >>> m.coins == {1: 2, 3: 1, 5: 1} - True - >>> m.change(2) - [1, 1] - >>> m.change(2) # not enough 1's remaining; return a 2 - [2] - >>> m.coins == {2: 1, 3: 1, 5: 1} - True - >>> m.change(8) # cannot use the 2 to make 8, so use 3 & 5 - [3, 5] - >>> m.coins == {2: 1, 8: 1} - True - >>> m.change(1) # return the penny passed in (it's the smallest) - [1] - >>> m.change(9) # return the 9 passed in (no change possible) - [9] - >>> m.coins == {2: 1, 8: 1} - True - >>> m.change(10) - [2, 8] - >>> m.coins == {10: 1} - True - - >>> m = ChangeMachine(9) - >>> [m.change(k) for k in [2, 2, 3]] - [[1, 1], [1, 1], [1, 1, 1]] - >>> m.coins == {1: 2, 2: 2, 3: 1} - True - >>> m.change(5) # Prefers [1, 1, 3] to [1, 2, 2] (more pennies) - [1, 1, 3] - >>> m.change(7) - [2, 5] - >>> m.coins == {2: 1, 7: 1} - True - """ - def __init__(self, pennies): - self.coins = {1: pennies} - - def change(self, coin): - """Return change for coin, removing the result from self.coins.""" - self.coins[coin] = 1 + self.coins.get(coin, 0) # Put the coin in the machine - result = make_change(coin, self.coins) - for c in result: - self.coins = remove_one(self.coins, c) - return result - diff --git a/lab/sol-lab06/lab06.zip b/lab/sol-lab06/lab06.zip deleted file mode 100644 index 37de7fbb25..0000000000 Binary files a/lab/sol-lab06/lab06.zip and /dev/null differ diff --git a/lab/sol-lab06/ok b/lab/sol-lab06/ok deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/lab/sol-lab06/ok and /dev/null differ diff --git a/lab/sol-lab06/tests/inheritance-abc.py b/lab/sol-lab06/tests/inheritance-abc.py deleted file mode 100644 index 61a80f218b..0000000000 --- a/lab/sol-lab06/tests/inheritance-abc.py +++ /dev/null @@ -1,40 +0,0 @@ -test = { - 'name': 'Inheritance ABCs', - 'points': 0, - 'suites': [ - { - 'type': 'wwpp', - 'cases': [ - { - 'code': """ - >>> class A: - ... x, y = 0, 0 - ... def __init__(self): - ... return - >>> class B(A): - ... def __init__(self): - ... return - >>> class C(A): - ... def __init__(self): - ... return - >>> print(A.x, B.x, C.x) - 0 0 0 - >>> B.x = 2 - >>> print(A.x, B.x, C.x) - 0 2 0 - >>> A.x += 1 - >>> print(A.x, B.x, C.x) - 1 2 1 - >>> obj = C() - >>> obj.y = 1 - >>> C.y == obj.y - False - >>> A.y = obj.y - >>> print(A.y, B.y, C.y, obj.y) - 1 1 1 1 - """ - } - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab06/tests/iterators-wwpd.py b/lab/sol-lab06/tests/iterators-wwpd.py deleted file mode 100644 index c87bd4c269..0000000000 --- a/lab/sol-lab06/tests/iterators-wwpd.py +++ /dev/null @@ -1,64 +0,0 @@ -test = { - 'name': 'Iterators', - 'points': 0, - 'suites': [ - { - 'type': 'wwpp', - 'cases': [ - { - 'code': """ - >>> # Enter StopIteration if StopIteration exception occurs, Error for other errors - >>> # Enter Iterator if the output is an iterator object. - >>> s = [1, 2, 3, 4] - >>> t = iter(s) - >>> next(s) - Error - >>> next(t) - 1 - >>> next(t) - 2 - >>> next(iter(s)) - 1 - >>> next(iter(s)) - 1 - >>> next(t) - 3 - >>> next(t) - 4 - """, - }, - { - 'code': """ - >>> r = range(6) - >>> r_iter = iter(r) - >>> next(r_iter) - 0 - >>> [x + 1 for x in r] - [1, 2, 3, 4, 5, 6] - >>> [x + 1 for x in r_iter] - [2, 3, 4, 5, 6] - >>> next(r_iter) - StopIteration - """, - }, - { - 'code': """ - >>> map_iter = map(lambda x : x + 10, range(5)) - >>> next(map_iter) - 10 - >>> next(map_iter) - 11 - >>> list(map_iter) - [12, 13, 14] - >>> for e in filter(lambda x : x % 4 == 0, range(1000, 1008)): - ... print(e) - 1000 - 1004 - >>> [x + y for x, y in zip([1, 2, 3], [4, 5, 6])] - [5, 7, 9] - """, - } - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab07/cardgame.py b/lab/sol-lab07/cardgame.py deleted file mode 100644 index e3eb6d1610..0000000000 --- a/lab/sol-lab07/cardgame.py +++ /dev/null @@ -1,134 +0,0 @@ -from classes import * -from cards import * - -try: - import readline -except ImportError: - pass - -########### -# Parsing # -########### - -def card_parse(line, handsize): - tokens = line.split() - if not tokens: - raise SyntaxError('No command given') - elif len(tokens) > 1: - raise SyntaxError('Too many inputs') - card_index = tokens.pop(0) - if not card_index.isdigit(): - raise SyntaxError('Wrong type of input') - card_index = int(card_index) - if card_index >= handsize or card_index < 0: - raise SyntaxError('Invalid card number') - return card_index - -def name_parse(line): - if not line: - raise SyntaxError('No command given') - return line - -######## -# REPL # -######## - -def read_eval_print_loop(): - while True: - try: - line = input('What is your name?> ') - name = name_parse(line) - break - except (KeyboardInterrupt, EOFError, SystemExit): # If you ctrl-c or ctrl-d - print('\nSee you next game!') - return - except SyntaxError as e: - print('ERROR:', e) - p1 = Player(player_deck, name) - p2 = Player(opponent_deck, 'Opponent') - print(WELCOME_MESSAGE) - duel = Game(p1, p2) - draw = True - while True: - if duel.game_won() == 1: - print(WIN_MESSAGE) - return - elif duel.game_won() == 2: - print(LOSE_MESSAGE) - return - print() - try: - if draw: - p1.draw() - p2.draw() - else: - draw = True - p1.display_hand() - print('Please enter the number next to the card you would like to play this round.') - line = input('card> ') - card_index = card_parse(line, len(p1.hand)) - duel.play_round(p1.play(card_index), p2.play_random()) - duel.display_scores() - except (KeyboardInterrupt, EOFError, SystemExit): # If you ctrl-c or ctrl-d - print('\nGood game. Bye!') - return - except AssertionError: # Deck out - if p1.deck.is_empty() and p2.deck.is_empty(): - print(TIE_MESSAGE) - return - elif p1.deck.is_empty(): - print(PLAYER_DECKOUT_MESSAGE) - return - else: - print(OPPONENT_DECKOUT_MESSAGE) - return - except SyntaxError as e: - print('ERROR:', e) - draw = False - -################# -# Configuration # -################# - -WELCOME_MESSAGE = """ -Welcome to Magic: The Lambda-ing! - -Your code has taken on a mind of its own and has -challenged you to a game of cards! If you need a refresher -on the rules, check out the section on the project page. - -Let's get this duel started, shall we? -""" - -WIN_MESSAGE = """ -You have vanquished your foe in a duel! - -Congratulations! You won this game of Magic: The Lambda-ing! -""" - -LOSE_MESSAGE = """ -You have been defeated by your foe in a duel! - -I'm sorry, you lost this game of Magic: The Lambda-ing. -""" - -TIE_MESSAGE = """ -You and your opponent have no cards left in your decks! - -You tied this game of Magic: The Lambda-ing. Who will win if you play again? -""" - -PLAYER_DECKOUT_MESSAGE = """ -You have no cards left in your deck! - -I'm sorry, you lost this game of Magic: The Lambda-ing. -""" - -OPPONENT_DECKOUT_MESSAGE = """ -Your opponent has no cards left in their deck! - -Congratulations! You won this game of Magic: The Lambda-ing! -""" - -if __name__ == '__main__': - read_eval_print_loop() \ No newline at end of file diff --git a/lab/sol-lab07/cards.py b/lab/sol-lab07/cards.py deleted file mode 100644 index 1832f69484..0000000000 --- a/lab/sol-lab07/cards.py +++ /dev/null @@ -1,42 +0,0 @@ -from classes import * - -standard_cards = [ - TACard('Rachel, Bringer of Boba', 1200, 2200), - TutorCard('RichRocks', 2100, 1000), - TutorCard('Apoλλo, Answerer of Ed', 2100, 1300), - TutorCard('Ratthew, King of the Rats', 2000, 1000), - TutorCard('Hans, Compiler of Chaos', 1600, 1500), - TACard('Cyrus, Cereal Consumer', 1500, 1500), - TutorCard('jade, jabbering jester', 2299, 1001), - TutorCard('Arnold, Description Here', 2000, 1200), - TutorCard('Sophia, Soda Slayer', 1200, 2200), - AICard('El Shroomster the Lamb, duh!', 1000, 2300), - TACard('Abby, Queen of Geese', 2000, 1300), - TutorCard('The λce', 1200, 2000), - AICard('Amber, Aetherweaver', 2100, 1200), - TACard('The Ace', 1700, 1500), - AICard('Bashwat, Brains of the Balawat', 1000, 2000), - AICard('Henry, Huggable Hipster', 1499, 1501), - AICard('The Smasher', 1300, 2000), - TutorCard('Ronald, Recursor of Recursion', 1000, 2300), - TutorCard('Albs, Ahri Ace', 1700, 1100), - TACard('Kevin, Random Schmoe Who Is Incredibly Powerful For Some Reason', 1800, 1500), - TACard('λaryn, λord of λambdas', 1500, 1700), - AICard('Alexander the Not So Great', 1600, 1700), - AICard('λnto\'s λI ', 2000, 1000), - TACard('Kotryna, Chaotic Neutral', 1300, 2100), - TutorCard('Darlnim, Darklord', 1600, 1500), - AICard('Jade, Lucky Fool', 2400, 1000), - AICard('Anshu, Chieftain of Recursion', 1100, 2300), - AICard('Rudy, Squirrel Whisperer', 1200, 2000), - TACard('Christina, Stick Breaker', 1900, 1100), - AICard('Adhvaith Thurvas, Caffeine-Powered Adverserial AI', 2100, 1100), - TACard('Bryce, Fuzzy Fire Flinger', 2200, 1200), - AICard('Zattack', 1000, 2100), - InstructorCard('DeNero, Antihero', 6650, 6650), - InstructorCard('Rao, Chaotic Wanderer', 0, 10000) -] - -standard_deck = Deck(standard_cards) -player_deck = standard_deck.copy() -opponent_deck = standard_deck.copy() \ No newline at end of file diff --git a/lab/sol-lab07/classes.py b/lab/sol-lab07/classes.py deleted file mode 100644 index 3684520896..0000000000 --- a/lab/sol-lab07/classes.py +++ /dev/null @@ -1,383 +0,0 @@ -# Magic the Lambda-ing! - -import random - -class Card: - cardtype = 'Staff' - - def __init__(self, name, attack, defense): - """ - Create a Card object with a name, attack, - and defense. - >>> staff_member = Card('staff', 400, 300) - >>> staff_member.name - 'staff' - >>> staff_member.attack - 400 - >>> staff_member.defense - 300 - >>> other_staff = Card('other', 300, 500) - >>> other_staff.attack - 300 - >>> other_staff.defense - 500 - """ - self.name = name - self.attack = attack - self.defense = defense - - def power(self, opponent_card): - """ - Calculate power as: - (player card's attack) - (opponent card's defense) - >>> staff_member = Card('staff', 400, 300) - >>> other_staff = Card('other', 300, 500) - >>> staff_member.power(other_staff) - -100 - >>> other_staff.power(staff_member) - 0 - >>> third_card = Card('third', 200, 400) - >>> staff_member.power(third_card) - 0 - >>> third_card.power(staff_member) - -100 - """ - return self.attack - opponent_card.defense - - - def effect(self, opponent_card, player, opponent): - """ - Cards have no default effect. - """ - return - - def __repr__(self): - """ - Returns a string which is a readable version of - a card, in the form: - : , [, ] - """ - return '{}: {}, [{}, {}]'.format(self.name, self.cardtype, self.attack, self.defense) - - def copy(self): - """ - Returns a copy of this card. - """ - return Card(self.name, self.attack, self.defense) - -class Player: - def __init__(self, deck, name): - """Initialize a Player object. - A Player starts the game by drawing 5 cards from their deck. Each turn, - a Player draws another card from the deck and chooses one to play. - >>> test_card = Card('test', 100, 100) - >>> test_deck = Deck([test_card.copy() for _ in range(6)]) - >>> test_player = Player(test_deck, 'tester') - >>> len(test_deck.cards) - 1 - >>> len(test_player.hand) - 5 - """ - self.deck = deck - self.name = name - self.hand = [deck.draw() for _ in range(5)] - - def draw(self): - """Draw a card from the player's deck and add it to their hand. - >>> test_card = Card('test', 100, 100) - >>> test_deck = Deck([test_card.copy() for _ in range(6)]) - >>> test_player = Player(test_deck, 'tester') - >>> test_player.draw() - >>> len(test_deck.cards) - 0 - >>> len(test_player.hand) - 6 - """ - assert not self.deck.is_empty(), 'Deck is empty!' - self.hand.append(self.deck.draw()) - - def play(self, index): - """Remove and return a card from the player's hand at the given INDEX. - >>> from cards import * - >>> test_player = Player(standard_deck, 'tester') - >>> ta1, ta2 = TACard("ta_1", 300, 400), TACard("ta_2", 500, 600) - >>> tutor1, tutor2 = TutorCard("t1", 200, 500), TutorCard("t2", 600, 400) - >>> test_player.hand = [ta1, ta2, tutor1, tutor2] - >>> test_player.play(0) is ta1 - True - >>> test_player.play(2) is tutor2 - True - >>> len(test_player.hand) - 2 - """ - return self.hand.pop(index) - - - def display_hand(self): - """ - Display the player's current hand to the user. - """ - print('Your hand:') - for card_index, displayed_card in zip(range(len(self.hand)),[str(card) for card in self.hand]): - indent = ' '*(5 - len(str(card_index))) - print(card_index, indent + displayed_card) - - def play_random(self): - """ - Play a random card from hand. - """ - return self.play(random.randrange(len(self.hand))) - -###################### -# Optional Questions # -###################### - -class AICard(Card): - cardtype = 'AI' - - def effect(self, opponent_card, player, opponent): - """ - Add the top two cards of your deck to your hand via drawing. - Once you have finished writing your code for this problem, - set implemented to True so that the text is printed when - playing an AICard. - - >>> from cards import * - >>> player1, player2 = Player(standard_deck.copy(), 'p1'), Player(standard_deck.copy(), 'p2') - >>> opponent_card = Card("other", 500, 500) - >>> test_card = AICard("AI Card", 500, 500) - >>> initial_deck_length = len(player1.deck.cards) - >>> initial_hand_size = len(player1.hand) - >>> test_card.effect(opponent_card, player1, player2) - AI Card allows me to draw two cards! - >>> initial_hand_size == len(player1.hand) - 2 - True - >>> initial_deck_length == len(player1.deck.cards) + 2 - True - """ - implemented = False - player.draw() - player.draw() - implemented = True - # You should add your implementation above this. - if implemented: - print(f"{self.name} allows me to draw two cards!") - - def copy(self): - """ - Create a copy of this card. - """ - return AICard(self.name, self.attack, self.defense) - -class TutorCard(Card): - cardtype = 'Tutor' - - def effect(self, opponent_card, player, opponent): - """ - Add a copy of the first card in your hand - to your hand, at the cost of losing the current - round. If there are no cards in hand, this card does - not add any cards, but still loses the round. To - implement the second part of this effect, a Tutor - card's power should be less than all non-Tutor cards. - - >>> from cards import * - >>> player1, player2 = Player(standard_deck.copy(), 'p1'), Player(standard_deck.copy(), 'p2') - >>> opponent_card = Card("other", 500, 500) - >>> test_card = TutorCard("Tutor Card", 10000, 10000) - >>> player1.hand = [Card("card1", 0, 100), Card("card2", 100, 0)] - >>> test_card.effect(opponent_card, player1, player2) - Tutor Card allows me to add a copy of a card to my hand! - >>> print(player1.hand) - [card1: Staff, [0, 100], card2: Staff, [100, 0], card1: Staff, [0, 100]] - >>> player1.hand[0] is player1.hand[2] # must add a copy! - False - >>> player1.hand = [] - >>> test_card.effect(opponent_card, player1, player2) - >>> print(player1.hand) # must not add a card if not available - [] - >>> test_card.power(opponent_card) < opponent_card.power(test_card) - True - """ - added = False - if len(player.hand) > 0: - player.hand += [player.hand[0].copy()] - added = True - # You should add your implementation above this. - if added: - print(f"{self.name} allows me to add a copy of a card to my hand!") - - def power(self, opponent_card): - return -float("inf") - - - def copy(self): - """ - Create a copy of this card. - """ - return TutorCard(self.name, self.attack, self.defense) - -class TACard(Card): - cardtype = 'TA' - - def effect(self, opponent_card, player, opponent, arg=None): - """ - Discard the card with the highest `power` in your hand, - and add the discarded card's attack and defense - to this card's own respective stats. - - >>> from cards import * - >>> player1, player2 = Player(standard_deck.copy(), 'p1'), Player(standard_deck.copy(), 'p2') - >>> opponent_card = Card("other", 500, 500) - >>> test_card = TACard("TA Card", 500, 500) - >>> player1.hand = [] - >>> test_card.effect(opponent_card, player1, player2) # if no cards in hand, no effect. - >>> print(test_card.attack, test_card.defense) - 500 500 - >>> player1.hand = [Card("card1", 0, 100), TutorCard("tutor", 10000, 10000), Card("card3", 100, 0)] - >>> test_card.effect(opponent_card, player1, player2) # must use card's power method. - TA Card discards card3 from my hand to increase its own power! - >>> print(player1.hand) - [card1: Staff, [0, 100], tutor: Tutor, [10000, 10000]] - >>> print(test_card.attack, test_card.defense) - 600 500 - """ - best_card = None - if len(player.hand) > 0: - best_card = max(player.hand, key = lambda c: c.power(opponent_card)) - self.attack += best_card.attack - self.defense += best_card.defense - player.hand.remove(best_card) - # You should add your implementation above this. - if best_card: - print(f"{self.name} discards {best_card.name} from my hand to increase its own power!") - - - def copy(self): - """ - Create a copy of this card. - """ - return TACard(self.name, self.attack, self.defense) - -class InstructorCard(Card): - cardtype = 'Instructor' - - def effect(self, opponent_card, player, opponent, arg=None): - """ - Survives multiple rounds, as long as it has a non-negative - attack or defense at the end of a round. At the beginning of the round, - its attack and defense are permanently reduced by 1000 each. - If this card would survive, it is added back to the hand. - - >>> from cards import * - >>> player1, player2 = Player(standard_deck.copy(), 'p1'), Player(standard_deck.copy(), 'p2') - >>> opponent_card = Card("other", 500, 500) - >>> test_card = InstructorCard("Instructor Card", 1000, 1000) - >>> player1.hand = [Card("card1", 0, 100)] - >>> test_card.effect(opponent_card, player1, player2) - Instructor Card returns to my hand! - >>> print(player1.hand) # survives with non-negative attack - [card1: Staff, [0, 100], Instructor Card: Instructor, [0, 0]] - >>> player1.hand = [Card("card1", 0, 100)] - >>> test_card.effect(opponent_card, player1, player2) - >>> print(player1.hand) - [card1: Staff, [0, 100]] - >>> print(test_card.attack, test_card.defense) - -1000 -1000 - """ - re_add = False - self.attack -= 1000 - self.defense -= 1000 - if max(self.attack, self.defense) >= 0: - re_add = True - player.hand += [self] - # You should add your implementation above this. - if re_add: - print(f"{self.name} returns to my hand!") - - def copy(self): - return InstructorCard(self.name, self.attack, self.defense) - - -######################################## -# Do not edit anything below this line # -######################################## - -class Deck: - def __init__(self, cards): - """ - With a list of cards as input, create a deck. - This deck should keep track of the cards it contains, and - we should be able to draw from the deck, taking a random - card out of it. - """ - self.cards = cards - - def draw(self): - """ - Pick a random card from the deck and remove it from the deck. - """ - assert self.cards, 'The deck is empty!' - rand_index = random.randrange(len(self.cards)) - return self.cards.pop(rand_index) - - def is_empty(self): - return len(self.cards) == 0 - - def copy(self): - """ - Create a copy of this deck. - """ - return Deck([card.copy() for card in self.cards]) - -class Game: - win_score = 8 - - def __init__(self, player1, player2): - """ - Initialize a game of Magic: the Lambda-ing. - """ - self.player1, self.player2 = player1, player2 - self.p1_score = 0 - self.p2_score = 0 - - def play_round(self, p1_card, p2_card): - """ - After each player picks a card, play them against - each other. - """ - p1_card.effect(p2_card, self.player1, self.player2) - p2_card.effect(p1_card, self.player2, self.player1) - p1_power = p1_card.power(p2_card) - p2_power = p2_card.power(p1_card) - if p1_power > p2_power: - # Player 1 wins the round. - self.p1_score += 1 - result = 'won' - elif p2_power > p1_power: - # Player 2 wins the round. - self.p2_score += 1 - result = 'lost' - else: - # This round is a draw. - result = 'tied' - # Display results to user. - print('You {} this round!'.format(result)) - print('{}\'s card: {}; Power: {}'.format(self.player1.name, p1_card, p1_power)) - print('Opponent\'s card: {}; Power: {}'.format(p2_card, p2_power)) - - def game_won(self): - """ - Check if the game is won and, if so, - which player won. - """ - if self.p1_score < self.win_score and self.p2_score < self.win_score: - return 0 - return 1 if self.p1_score > self.p2_score else 2 - - def display_scores(self): - """ - Display players' scores to the user. - """ - print('{}\'s score: {}'.format(self.player1.name, self.p1_score)) - print('Opponent\'s score: {}'.format(self.p2_score)) \ No newline at end of file diff --git a/lab/sol-lab07/construct_check.py b/lab/sol-lab07/construct_check.py deleted file mode 100644 index bee096e7e4..0000000000 --- a/lab/sol-lab07/construct_check.py +++ /dev/null @@ -1,180 +0,0 @@ -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/lab/sol-lab07/index.html b/lab/sol-lab07/index.html deleted file mode 100644 index 14e87399f0..0000000000 --- a/lab/sol-lab07/index.html +++ /dev/null @@ -1,777 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Lab 7 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Lab 7 Solutions - - - - - - -

-
- -

Solution Files

- - - -

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.

- - -

YouTube link

-
- - -

Inheritance

- - -

Consult the drop-down if you need a refresher on Inheritance. It's -okay to skip directly to the questions and refer back -here should you get stuck. - - -

- - - -

To avoid redefining attributes and methods for similar classes, we can write a -single base class from which more specialized classes inherit. For -example, we can write a class called Pet and define Dog as a subclass of -Pet:

- -
class Pet:
-
-    def __init__(self, name, owner):
-        self.is_alive = True    # It's alive!!!
-        self.name = name
-        self.owner = owner
-
-    def eat(self, thing):
-        print(self.name + " ate a " + str(thing) + "!")
-
-    def talk(self):
-        print(self.name)
-
-class Dog(Pet):
-
-    def talk(self):
-        super().talk()
-        print('This Dog says woof!')
- - - -

Inheritance represents a hierarchical relationship between two or more -classes where one class is a more specific version of the other: -a dog is a pet -(We use is a to describe this sort of relationship in OOP languages, -and not to refer to the Python is operator).

- -

Since Dog inherits from Pet, the Dog class will also inherit the -Pet class's methods, so we don't have to redefine __init__ or eat. -We do want each Dog to talk in a Dog-specific way, -so we can override the talk method.

- -

We can use super() to refer to the superclass of self, -and access any superclass methods as if we were an instance of the superclass. -For example, super().talk() in the Dog class will call the talk -method from the Pet class, but passing the Dog instance as the self.

- -
- - -

Q1: WWPD: Inheritance ABCs

- - -

Important: -For all WWPD questions, type Function if you believe the answer is -<function...>, Error if it errors, and Nothing if nothing is displayed.

- -

Use Ok to test your knowledge with the following "What Would Python Display?" questions:

python3 ok -q inheritance-abc -u
- -
- -
>>> class A:
-...   x, y = 0, 0
-...   def __init__(self):
-...         return
->>> class B(A):
-...   def __init__(self):
-...         return
->>> class C(A):
-...   def __init__(self):
-...         return
->>> print(A.x, B.x, C.x)
-
______
______
->>> B.x = 2 ->>> print(A.x, B.x, C.x) -
______
______
->>> A.x += 1 ->>> print(A.x, B.x, C.x) -
______
______
->>> obj = C() ->>> obj.y = 1 ->>> C.y == obj.y -
______
______
->>> A.y = obj.y ->>> print(A.y, B.y, C.y, obj.y) -
______
______
- - - - -

Class Practice

- - -

Let's say we'd like to model a bank account that can handle interactions -such as depositing funds or gaining interest on current funds. -In the following questions, we will be building off of the Account class. -Here's our current definition of the class:

- -
class Account:
-    """An account has a balance and a holder.
-    >>> a = Account('John')
-    >>> a.deposit(10)
-    10
-    >>> a.balance
-    10
-    >>> a.interest
-    0.02
-    >>> a.time_to_retire(10.25)  # 10 -> 10.2 -> 10.404
-    2
-    >>> a.balance                # Calling time_to_retire method should not change the balance
-    10
-    >>> a.time_to_retire(11)     # 10 -> 10.2 -> ... -> 11.040808032
-    5
-    >>> a.time_to_retire(100)
-    117
-    """
-    max_withdrawal = 10
-    interest = 0.02
-
-    def __init__(self, account_holder):
-        self.balance = 0
-        self.holder = account_holder
-
-    def deposit(self, amount):
-        self.balance = self.balance + amount
-        return self.balance
-
-    def withdraw(self, amount):
-        if amount > self.balance:
-            return "Insufficient funds"
-        if amount > self.max_withdrawal:
-            return "Can't withdraw that amount"
-        self.balance = self.balance - amount
-        return self.balance
- - -

Q2: Retirement

- - -

Add a time_to_retire method to the Account class. -This method takes in an amount and returns how many years the holder would -need to wait in order for the current balance to grow to at least amount, -assuming that the bank adds the interest (calculated as the current balance -multiplied by the interest rate) to the balance at the end of each year.

- -

Note: Calling the time_to_retire method should not change the account balance.

- - - -
    def time_to_retire(self, amount):
-        """Return the number of years until balance would grow to amount."""
-        assert self.balance > 0 and amount > 0 and self.interest > 0
-
future = self.balance - years = 0 - while future < amount: - future += self.interest * future - years += 1 - return years
- -
- -

Use Ok to test your code:

python3 ok -q Account
- -
- -
- -

We take of our current balance, and simulate the growth from interest over -many years. We stop once we hit the target value.

- -

Note that the problem solving procedure does not differ very much from an -non OOP problem. The main difference here is make sure that we do not -change the account balance while in the process of calculating the future -balance. Therefore, something along these lines is necessary:

- -
future = self.balance
- -

Video walkthrough:

YouTube link

- -
- - -

Q3: FreeChecking

- - -

Implement the FreeChecking class, which is like the Account class -except that it charges a withdraw fee withdraw_fee after -withdrawing free_withdrawals number of times. -If a withdrawal is unsuccessful, it still counts towards the number of free -withdrawals remaining, but no fee for the withdrawal will be charged.

- - - -
class FreeChecking(Account):
-    """A bank account that charges for withdrawals, but the first two are free!
-    >>> ch = FreeChecking('Jack')
-    >>> ch.balance = 20
-    >>> ch.withdraw(100)  # First one's free. Still counts as a free withdrawal even though it was unsuccessful
-    'Insufficient funds'
-    >>> ch.withdraw(3)    # Second withdrawal is also free
-    17
-    >>> ch.balance
-    17
-    >>> ch.withdraw(3)    # Ok, two free withdrawals is enough, as free_withdrawals is only 2
-    13
-    >>> ch.withdraw(3)
-    9
-    >>> ch2 = FreeChecking('John')
-    >>> ch2.balance = 10
-    >>> ch2.withdraw(3) # No fee
-    7
-    >>> ch.withdraw(3)  # ch still charges a fee
-    5
-    >>> ch.withdraw(5)  # Not enough to cover fee + withdraw
-    'Insufficient funds'
-    """
-    withdraw_fee = 1
-    free_withdrawals = 2
-
-
def __init__(self, account_holder): - super().__init__(account_holder) - self.withdrawals = 0 - - def withdraw(self, amount): - self.withdrawals += 1 - fee = 0 - if self.withdrawals > self.free_withdrawals: - fee = self.withdraw_fee - return super().withdraw(amount + fee) - - # Alternative solution where you don't need to include init. - # Check out the video solution for more. - def withdraw(self, amount): - self.free_withdrawals -= 1 - if self.free_withdrawals >= 0: - return super().withdraw(amount) - return super().withdraw(amount + self.withdraw_fee)
- -
- -

Use Ok to test your code:

python3 ok -q FreeChecking
- -
- -
- -

We can take advantage of inheritance to make sure we add just what we need -to withdraw.

- -
    -
  • For starters, a withdrawal with a fee is the same as the original - withdraw amount plus the amount from the fee. We can therefore represent - a FreeChecking withdraw as a "regular" Account withdraw in this way.
  • -
  • On top of the note from before, we need to do a little bit of extra - bookkeeping to make sure the first few withdrawals do not add the extra - fee. We can either create a new instance attribute or modify an existing - one.
  • -
- -

Video walkthrough:

YouTube link

- -
- - -

Linked Lists

- - -

Consult the drop-down if you need a refresher on Linked Lists. It's -okay to skip directly to the questions and refer back -here should you get stuck.

- - - -
- -

A linked list is a data structure for storing a sequence of values. It is more -efficient than a regular built-in list for certain operations, such as inserting -a value in the middle of a long list. Linked lists are not built in, and so we -define a class called Link to represent them. -A linked list is either a Link instance or Link.empty -(which represents an empty linked list).

- -

A instance of Link has two instance attributes, first and rest.

- -

The rest attribute of a Link instance should always be a linked list: either -another Link instance or Link.empty. It SHOULD NEVER be None.

- -

To check if a linked list is empty, compare it to Link.empty. Since there is only -ever one empty list, we can use is to compare, but == would work too.

- -
def is_empty(s):
-    """Return whether linked list s is empty."""
-    return s is 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.
  • -
- -
- - -

Q4: WWPD: Linked Lists

- - -

Read over the Link class. Make sure you understand the doctests.

- -

Use Ok to test your knowledge with the following "What Would Python Display?" -questions:

- -
python3 ok -q link -u
- -

Enter Function if you believe the answer is <function ...>, Error if it -errors, and Nothing if nothing is displayed.

- -

If you get stuck, try drawing out the box-and-pointer diagram for the linked -list on a piece of paper or loading the Link class into the interpreter -with python3 -i lab08.py.

- -
>>> link = Link(1000)
->>> link.first
-
______
1000
->>> link.rest is Link.empty -
______
True
->>> link = Link(1000, 2000) -
______
AssertionError
->>> link = Link(1000, Link()) -
______
TypeError
- - - -
>>> link = Link(1, Link(2, Link(3)))
->>> link.first
-
______
1
->>> link.rest.first -
______
2
->>> link.rest.rest.rest is Link.empty -
______
True
->>> link.first = 9001 ->>> link.first -
______
9001
->>> link.rest = link.rest.rest ->>> link.rest.first -
______
3
->>> link = Link(1) ->>> link.rest = link ->>> link.rest.rest is Link.empty -
______
False
->>> link.rest.rest.rest.rest.first -
______
1
->>> link = Link(2, Link(3, Link(4))) ->>> link2 = Link(1, link) ->>> link2.first -
______
1
->>> link2.rest.first -
______
2
- - - -
>>> link = Link(5, Link(6, Link(7)))
->>> link                 # Look at the __repr__ method of Link
-
______
Link(5, Link(6, Link(7)))
->>> print(link) # Look at the __str__ method of Link -
______
<5 6 7>
- - - - - - - -

Write a function duplicate_link that takes in a linked list s and a value val. -It mutates s so that each element equal to val is followed by an additional val (a duplicate copy). -It returns None.

- -

Note: In order to insert a link into a linked list, reassign the rest attribute of the Link instances that have val as their first. Try drawing out a doctest to visualize!

- - - -
def duplicate_link(s, val):
-    """Mutates s so that each element equal to val is followed by another val.
-
-    >>> x = Link(5, Link(4, Link(5)))
-    >>> duplicate_link(x, 5)
-    >>> x
-    Link(5, Link(5, Link(4, Link(5, Link(5)))))
-    >>> y = Link(2, Link(4, Link(6, Link(8))))
-    >>> duplicate_link(y, 10)
-    >>> y
-    Link(2, Link(4, Link(6, Link(8))))
-    >>> z = Link(1, Link(2, (Link(2, Link(3)))))
-    >>> duplicate_link(z, 2) # ensures that back to back links with val are both duplicated
-    >>> z
-    Link(1, Link(2, Link(2, Link(2, Link(2, Link(3))))))
-    """
-
if s is Link.empty: - return - elif s.first == val: - remaining = s.rest - s.rest = Link(val, remaining) - duplicate_link(remaining, val) - else: - duplicate_link(s.rest, val)
- -
- -

Use Ok to test your code:

python3 ok -q duplicate_link
- -
- - -

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.

- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/lab/sol-lab07/lab07.ok b/lab/sol-lab07/lab07.ok deleted file mode 100644 index 3aad7b81f9..0000000000 --- a/lab/sol-lab07/lab07.ok +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "Lab 7", - "endpoint": "cal/cs61a/fa24/lab07", - "src": [ - "lab07.py", - "classes.py" - ], - "tests": { - "lab*.py": "doctest", - "tests/*.py": "ok_test", - "classes.py": "doctest" - }, - "default_tests": [ - "Account", - "FreeChecking", - "duplicate_link" - ], - "protocols": [ - "restore", - "file_contents", - "unlock", - "grading", - "analytics", - "backup" - ] -} \ No newline at end of file diff --git a/lab/sol-lab07/lab07.py b/lab/sol-lab07/lab07.py deleted file mode 100644 index b63aaac131..0000000000 --- a/lab/sol-lab07/lab07.py +++ /dev/null @@ -1,161 +0,0 @@ -class Account: - """An account has a balance and a holder. - >>> a = Account('John') - >>> a.deposit(10) - 10 - >>> a.balance - 10 - >>> a.interest - 0.02 - >>> a.time_to_retire(10.25) # 10 -> 10.2 -> 10.404 - 2 - >>> a.balance # Calling time_to_retire method should not change the balance - 10 - >>> a.time_to_retire(11) # 10 -> 10.2 -> ... -> 11.040808032 - 5 - >>> a.time_to_retire(100) - 117 - """ - max_withdrawal = 10 - interest = 0.02 - - def __init__(self, account_holder): - self.balance = 0 - self.holder = account_holder - - def deposit(self, amount): - self.balance = self.balance + amount - return self.balance - - def withdraw(self, amount): - if amount > self.balance: - return "Insufficient funds" - if amount > self.max_withdrawal: - return "Can't withdraw that amount" - self.balance = self.balance - amount - return self.balance - - def time_to_retire(self, amount): - """Return the number of years until balance would grow to amount.""" - assert self.balance > 0 and amount > 0 and self.interest > 0 - future = self.balance - years = 0 - while future < amount: - future += self.interest * future - years += 1 - return years - - -class FreeChecking(Account): - """A bank account that charges for withdrawals, but the first two are free! - >>> ch = FreeChecking('Jack') - >>> ch.balance = 20 - >>> ch.withdraw(100) # First one's free. Still counts as a free withdrawal even though it was unsuccessful - 'Insufficient funds' - >>> ch.withdraw(3) # Second withdrawal is also free - 17 - >>> ch.balance - 17 - >>> ch.withdraw(3) # Ok, two free withdrawals is enough, as free_withdrawals is only 2 - 13 - >>> ch.withdraw(3) - 9 - >>> ch2 = FreeChecking('John') - >>> ch2.balance = 10 - >>> ch2.withdraw(3) # No fee - 7 - >>> ch.withdraw(3) # ch still charges a fee - 5 - >>> ch.withdraw(5) # Not enough to cover fee + withdraw - 'Insufficient funds' - """ - withdraw_fee = 1 - free_withdrawals = 2 - - def __init__(self, account_holder): - super().__init__(account_holder) - self.withdrawals = 0 - - def withdraw(self, amount): - self.withdrawals += 1 - fee = 0 - if self.withdrawals > self.free_withdrawals: - fee = self.withdraw_fee - return super().withdraw(amount + fee) - - # Alternative solution where you don't need to include init. - # Check out the video solution for more. - def withdraw(self, amount): - self.free_withdrawals -= 1 - if self.free_withdrawals >= 0: - return super().withdraw(amount) - return super().withdraw(amount + self.withdraw_fee) - - -def duplicate_link(s, val): - """Mutates s so that each element equal to val is followed by another val. - - >>> x = Link(5, Link(4, Link(5))) - >>> duplicate_link(x, 5) - >>> x - Link(5, Link(5, Link(4, Link(5, Link(5))))) - >>> y = Link(2, Link(4, Link(6, Link(8)))) - >>> duplicate_link(y, 10) - >>> y - Link(2, Link(4, Link(6, Link(8)))) - >>> z = Link(1, Link(2, (Link(2, Link(3))))) - >>> duplicate_link(z, 2) # ensures that back to back links with val are both duplicated - >>> z - Link(1, Link(2, Link(2, Link(2, Link(2, Link(3)))))) - """ - if s is Link.empty: - return - elif s.first == val: - remaining = s.rest - s.rest = Link(val, remaining) - duplicate_link(remaining, val) - else: - duplicate_link(s.rest, val) - - -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/lab/sol-lab07/lab07.zip b/lab/sol-lab07/lab07.zip deleted file mode 100644 index 6ce4ed89f2..0000000000 Binary files a/lab/sol-lab07/lab07.zip and /dev/null differ diff --git a/lab/sol-lab07/ok b/lab/sol-lab07/ok deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/lab/sol-lab07/ok and /dev/null differ diff --git a/lab/sol-lab07/tests/inheritance-abc.py b/lab/sol-lab07/tests/inheritance-abc.py deleted file mode 100644 index 61a80f218b..0000000000 --- a/lab/sol-lab07/tests/inheritance-abc.py +++ /dev/null @@ -1,40 +0,0 @@ -test = { - 'name': 'Inheritance ABCs', - 'points': 0, - 'suites': [ - { - 'type': 'wwpp', - 'cases': [ - { - 'code': """ - >>> class A: - ... x, y = 0, 0 - ... def __init__(self): - ... return - >>> class B(A): - ... def __init__(self): - ... return - >>> class C(A): - ... def __init__(self): - ... return - >>> print(A.x, B.x, C.x) - 0 0 0 - >>> B.x = 2 - >>> print(A.x, B.x, C.x) - 0 2 0 - >>> A.x += 1 - >>> print(A.x, B.x, C.x) - 1 2 1 - >>> obj = C() - >>> obj.y = 1 - >>> C.y == obj.y - False - >>> A.y = obj.y - >>> print(A.y, B.y, C.y, obj.y) - 1 1 1 1 - """ - } - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab07/tests/link.py b/lab/sol-lab07/tests/link.py deleted file mode 100644 index 5e35e4989c..0000000000 --- a/lab/sol-lab07/tests/link.py +++ /dev/null @@ -1,65 +0,0 @@ -test = { - 'name': 'Link', - 'points': 0, - 'suites': [ - { - 'type': 'wwpp', - 'cases': [ - { - 'code': """ - >>> from lab08 import * - >>> link = Link(1000) - >>> link.first - 1000 - >>> link.rest is Link.empty - True - >>> link = Link(1000, 2000) - Error - >>> link = Link(1000, Link()) - Error - """, - }, - { - 'code': """ - >>> from lab08 import * - >>> link = Link(1, Link(2, Link(3))) - >>> link.first - 1 - >>> link.rest.first - 2 - >>> link.rest.rest.rest is Link.empty - True - >>> link.first = 9001 - >>> link.first - 9001 - >>> link.rest = link.rest.rest - >>> link.rest.first - 3 - >>> link = Link(1) - >>> link.rest = link - >>> link.rest.rest is Link.empty - False - >>> link.rest.rest.rest.rest.first - 1 - >>> link = Link(2, Link(3, Link(4))) - >>> link2 = Link(1, link) - >>> link2.first - 1 - >>> link2.rest.first - 2 - """, - }, - { - 'code': """ - >>> from lab08 import * - >>> link = Link(5, Link(6, Link(7))) - >>> link # Look at the __repr__ method of Link - Link(5, Link(6, Link(7))) - >>> print(link) # Look at the __str__ method of Link - <5 6 7> - """, - }, - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab08/construct_check.py b/lab/sol-lab08/construct_check.py deleted file mode 100644 index bee096e7e4..0000000000 --- a/lab/sol-lab08/construct_check.py +++ /dev/null @@ -1,180 +0,0 @@ -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/lab/sol-lab08/index.html b/lab/sol-lab08/index.html deleted file mode 100644 index 32b104361a..0000000000 --- a/lab/sol-lab08/index.html +++ /dev/null @@ -1,653 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Lab 8 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Lab 8 Solutions - - - - - - -

-
- -

Solution Files

- - - -

Topics

- - -

Consult this section if you need a refresher on the material for this lab. It's -okay to skip directly to the questions and refer back -here should you get stuck.

- -
- - -
- - -

Mutable Trees

- - -

A Tree instance has two instance attributes:

- -
    -
  • label is the value stored at the root of the tree.
  • -
  • branches is a list of Tree instances that hold the labels in the rest of the tree.
  • -
- -

The Tree class (with its __repr__ and __str__ methods omitted) is defined as:

- -
class Tree:
-    """
-    >>> t = Tree(3, [Tree(2, [Tree(5)]), Tree(4)])
-    >>> t.label
-    3
-    >>> t.branches[0].label
-    2
-    >>> t.branches[1].is_leaf()
-    True
-    """
-    def __init__(self, label, branches=[]):
-        for b in branches:
-            assert isinstance(b, Tree)
-        self.label = label
-        self.branches = list(branches)
-
-    def is_leaf(self):
-        return not self.branches
- -

To construct a Tree instance from a label x (any value) and a list of branches bs (a list of Tree instances) and give it the name t, write t = Tree(x, bs).

- -

For a tree t:

- -
    -
  • Its root label can be any value, and t.label evaluates to it.
  • -
  • Its branches are always Tree instances, and t.branches evaluates to the list of its branches.
  • -
  • t.is_leaf() returns True if t.branches is empty and False otherwise.
  • -
  • To construct a leaf with label x, write Tree(x).
  • -
- -

Displaying a tree t:

- -
    -
  • repr(t) returns a Python expression that evaluates to an equivalent tree.
  • -
  • str(t) returns one line for each label indented once more than its parent with children below their parents.
  • -
- -
>>> t = Tree(3, [Tree(1, [Tree(4), Tree(1)]), Tree(5, [Tree(9)])])
-
->>> t         # displays the contents of repr(t)
-Tree(3, [Tree(1, [Tree(4), Tree(1)]), Tree(5, [Tree(9)])])
-
->>> print(t)  # displays the contents of str(t)
-3
-  1
-    4
-    1
-  5
-    9
- - - -

Changing (also known as mutating) a tree t:

- -
    -
  • t.label = y changes the root label of t to y (any value).
  • -
  • t.branches = ns changes the branches of t to ns (a list of Tree instances).
  • -
  • Mutation of t.branches will change t. For example, - t.branches.append(Tree(y)) will add a leaf labeled y as the right-most - branch.
  • -
  • Mutation of any branch in t will change t. For example, - t.branches[0].label = y will change the root label of the left-most branch - to y.
  • -
- -
>>> t.label = 3.0
->>> t.branches[1].label = 5.0
->>> t.branches.append(Tree(2, [Tree(6)]))
->>> print(t)
-3.0
-  1
-    4
-    1
-  5.0
-    9
-  2
-    6
- - - -

Here is a summary of the differences between the tree data abstraction implemented as a functional abstraction vs. implemented as a class:

- - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Tree constructor and selector functionsTree class
Constructing a treeTo construct a tree given a label and a list of branches, we call tree(label, branches)To construct a tree object given a label and a list of branches, we call Tree(label, branches) (which calls the Tree.__init__ method).
Label and branchesTo get the label or branches of a tree t, we call label(t) or branches(t) respectivelyTo get the label or branches of a tree t, we access the instance attributes t.label or t.branches respectively.
MutabilityThe functional tree data abstraction is immutable (without violating its abstraction barrier) because we cannot assign values to call expressionsThe label and branches attributes of a Tree instance can be reassigned, mutating the tree.
Checking if a tree is a leafTo check whether a tree t is a leaf, we call the function is_leaf(t)To check whether a tree t is a leaf, we call the method t.is_leaf(). This method can only be called on Tree objects.
-
- - - -
-
- - -

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.

- - -

YouTube link

-
- - -

Mutable Trees

- - - -

Q1: WWPD: Trees

- - -

Read over the Tree class in lab08.py. Make sure you understand the -doctests.

- -

Use Ok to test your knowledge with the following "What Would Python Display?" -questions:

- -
python3 ok -q trees-wwpd -u
- -

Enter Function if you believe the answer is <function ...>, Error if it -errors, and Nothing if nothing is displayed. Recall that Tree instances -will be displayed the same way they are constructed.

- -
>>> t = Tree(1, Tree(2))
-
______
Error
->>> t = Tree(1, [Tree(2)]) ->>> t.label -
______
1
->>> t.branches[0] -
______
Tree(2)
->>> t.branches[0].label -
______
2
->>> t.label = t.branches[0].label ->>> t -
______
Tree(2, [Tree(2)])
->>> t.branches.append(Tree(4, [Tree(8)])) ->>> len(t.branches) -
______
2
->>> t.branches[0] -
______
Tree(2)
->>> t.branches[1] -
______
Tree(4, [Tree(8)])
- - - - -

Q2: Cumulative Mul

- - -

Write a function cumulative_mul that mutates the Tree t so that each node's -label is replaced by the product of its label and the labels of all its descendents.

- -

Hint: -Be careful of the order in which you mutate the current node's label and process its subtrees; which one should come first?

- - - -
def cumulative_mul(t):
-    """Mutates t so that each node's label becomes the product of its own
-    label and all labels in the corresponding subtree rooted at t.
-
-    >>> t = Tree(1, [Tree(3, [Tree(5)]), Tree(7)])
-    >>> cumulative_mul(t)
-    >>> t
-    Tree(105, [Tree(15, [Tree(5)]), Tree(7)])
-    >>> otherTree = Tree(2, [Tree(1, [Tree(3), Tree(4), Tree(5)]), Tree(6, [Tree(7)])])
-    >>> cumulative_mul(otherTree)
-    >>> otherTree
-    Tree(5040, [Tree(60, [Tree(3), Tree(4), Tree(5)]), Tree(42, [Tree(7)])])
-    """
-
for b in t.branches: - cumulative_mul(b) - total = t.label - for b in t.branches: - total *= b.label - t.label = total - -# Alternate solution using only one loop -def cumulative_mul(t): - for b in t.branches: - cumulative_mul(b) - t.label *= b.label
- -
- -

Use Ok to test your code:

python3 ok -q cumulative_mul
- -
- - -

Q3: Prune Small

- - -

Removing some nodes from a tree is called pruning the tree.

- -

Complete the function prune_small that takes in a Tree t and a number n. -For each node with more than n branches, keep only the n branches with the -smallest labels and remove (prune) the rest.

- -

Hint: The max function takes in an iterable as well as an optional key argument (which takes in a one-argument function). -For example, max([-7, 2, -1], key=abs) would return -7 since abs(-7) is greater than abs(2) and abs(-1).

- - - -
def prune_small(t, n):
"""Prune the tree mutatively, keeping only the n branches - of each node with the smallest labels. - - >>> t1 = Tree(6) - >>> prune_small(t1, 2) - >>> t1 - Tree(6) - >>> t2 = Tree(6, [Tree(3), Tree(4)]) - >>> prune_small(t2, 1) - >>> t2 - Tree(6, [Tree(3)]) - >>> t3 = Tree(6, [Tree(1), Tree(3, [Tree(1), Tree(2), Tree(3)]), Tree(5, [Tree(3), Tree(4)])]) - >>> prune_small(t3, 2) - >>> t3 - Tree(6, [Tree(1), Tree(3, [Tree(1), Tree(2)])]) - """ -
while len(t.branches) > n:
largest = max(t.branches, key=lambda x: x.label)
t.branches.remove(largest)
for b in t.branches:
prune_small(b, n)
- -
- -

Use Ok to test your code:

python3 ok -q prune_small
- -
- - -

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.

- - -

Optional Questions

- - - -

Q4: Delete

- - -

Implement delete, which takes a Tree t and removes all non-root nodes labeled x. -The parent of each remaining node is its nearest ancestor that was not removed. -The root node is never removed, even if its label is x.

- - - -
def delete(t, x):
-    """Remove all nodes labeled x below the root within Tree t. When a non-leaf
-    node is deleted, the deleted node's children become children of its parent.
-
-    The root node will never be removed.
-
-    >>> t = Tree(3, [Tree(2, [Tree(2), Tree(2)]), Tree(2), Tree(2, [Tree(2, [Tree(2), Tree(2)])])])
-    >>> delete(t, 2)
-    >>> t
-    Tree(3)
-    >>> t = Tree(1, [Tree(2, [Tree(4, [Tree(2)]), Tree(5)]), Tree(3, [Tree(6), Tree(2)]), Tree(4)])
-    >>> delete(t, 2)
-    >>> t
-    Tree(1, [Tree(4), Tree(5), Tree(3, [Tree(6)]), Tree(4)])
-    >>> t = Tree(1, [Tree(2, [Tree(4), Tree(5)]), Tree(3, [Tree(6), Tree(2)]), Tree(2, [Tree(6),  Tree(2), Tree(7), Tree(8)]), Tree(4)])
-    >>> delete(t, 2)
-    >>> t
-    Tree(1, [Tree(4), Tree(5), Tree(3, [Tree(6)]), Tree(6), Tree(7), Tree(8), Tree(4)])
-    """
-    new_branches = []
-
for b in t.branches:
delete(b, x)
if b.label == x: -
new_branches.extend(b.branches)
else: -
new_branches.append(b)
t.branches = new_branches
- -
- -

Use Ok to test your code:

python3 ok -q delete
- -
- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/lab/sol-lab08/lab08.ok b/lab/sol-lab08/lab08.ok deleted file mode 100644 index 18cbfc0a6d..0000000000 --- a/lab/sol-lab08/lab08.ok +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "Lab 8", - "endpoint": "cal/cs61a/fa24/lab08", - "src": [ - "lab08.py" - ], - "tests": { - "lab*.py": "doctest", - "tests/*.py": "ok_test" - }, - "default_tests": [ - "trees-wwpd", - "cumulative_mul", - "prune_small" - ], - "protocols": [ - "restore", - "file_contents", - "unlock", - "grading", - "analytics", - "backup" - ] -} \ No newline at end of file diff --git a/lab/sol-lab08/lab08.py b/lab/sol-lab08/lab08.py deleted file mode 100644 index e34e44a909..0000000000 --- a/lab/sol-lab08/lab08.py +++ /dev/null @@ -1,114 +0,0 @@ -def cumulative_mul(t): - """Mutates t so that each node's label becomes the product of its own - label and all labels in the corresponding subtree rooted at t. - - >>> t = Tree(1, [Tree(3, [Tree(5)]), Tree(7)]) - >>> cumulative_mul(t) - >>> t - Tree(105, [Tree(15, [Tree(5)]), Tree(7)]) - >>> otherTree = Tree(2, [Tree(1, [Tree(3), Tree(4), Tree(5)]), Tree(6, [Tree(7)])]) - >>> cumulative_mul(otherTree) - >>> otherTree - Tree(5040, [Tree(60, [Tree(3), Tree(4), Tree(5)]), Tree(42, [Tree(7)])]) - """ - for b in t.branches: - cumulative_mul(b) - total = t.label - for b in t.branches: - total *= b.label - t.label = total - -# Alternate solution using only one loop -def cumulative_mul(t): - for b in t.branches: - cumulative_mul(b) - t.label *= b.label - - -def prune_small(t, n): - """Prune the tree mutatively, keeping only the n branches - of each node with the smallest labels. - - >>> t1 = Tree(6) - >>> prune_small(t1, 2) - >>> t1 - Tree(6) - >>> t2 = Tree(6, [Tree(3), Tree(4)]) - >>> prune_small(t2, 1) - >>> t2 - Tree(6, [Tree(3)]) - >>> t3 = Tree(6, [Tree(1), Tree(3, [Tree(1), Tree(2), Tree(3)]), Tree(5, [Tree(3), Tree(4)])]) - >>> prune_small(t3, 2) - >>> t3 - Tree(6, [Tree(1), Tree(3, [Tree(1), Tree(2)])]) - """ - while len(t.branches) > n: - largest = max(t.branches, key=lambda x: x.label) - t.branches.remove(largest) - for b in t.branches: - prune_small(b, n) - - -def delete(t, x): - """Remove all nodes labeled x below the root within Tree t. When a non-leaf - node is deleted, the deleted node's children become children of its parent. - - The root node will never be removed. - - >>> t = Tree(3, [Tree(2, [Tree(2), Tree(2)]), Tree(2), Tree(2, [Tree(2, [Tree(2), Tree(2)])])]) - >>> delete(t, 2) - >>> t - Tree(3) - >>> t = Tree(1, [Tree(2, [Tree(4, [Tree(2)]), Tree(5)]), Tree(3, [Tree(6), Tree(2)]), Tree(4)]) - >>> delete(t, 2) - >>> t - Tree(1, [Tree(4), Tree(5), Tree(3, [Tree(6)]), Tree(4)]) - >>> t = Tree(1, [Tree(2, [Tree(4), Tree(5)]), Tree(3, [Tree(6), Tree(2)]), Tree(2, [Tree(6), Tree(2), Tree(7), Tree(8)]), Tree(4)]) - >>> delete(t, 2) - >>> t - Tree(1, [Tree(4), Tree(5), Tree(3, [Tree(6)]), Tree(6), Tree(7), Tree(8), Tree(4)]) - """ - new_branches = [] - for b in t.branches: - delete(b, x) - if b.label == x: - new_branches.extend(b.branches) - else: - new_branches.append(b) - t.branches = new_branches - - -class Tree: - """ - >>> t = Tree(3, [Tree(2, [Tree(5)]), Tree(4)]) - >>> t.label - 3 - >>> t.branches[0].label - 2 - >>> t.branches[1].is_leaf() - True - """ - def __init__(self, label, branches=[]): - for b in branches: - assert isinstance(b, Tree) - self.label = label - self.branches = list(branches) - - def is_leaf(self): - return not self.branches - - def __repr__(self): - if self.branches: - branch_str = ', ' + repr(self.branches) - else: - branch_str = '' - return 'Tree({0}{1})'.format(self.label, branch_str) - - def __str__(self): - def print_tree(t, indent=0): - tree_str = ' ' * indent + str(t.label) + "\n" - for b in t.branches: - tree_str += print_tree(b, indent + 1) - return tree_str - return print_tree(self).rstrip() - diff --git a/lab/sol-lab08/lab08.zip b/lab/sol-lab08/lab08.zip deleted file mode 100644 index 870e71cc87..0000000000 Binary files a/lab/sol-lab08/lab08.zip and /dev/null differ diff --git a/lab/sol-lab08/link_class.py b/lab/sol-lab08/link_class.py deleted file mode 100644 index a343c8b83a..0000000000 --- a/lab/sol-lab08/link_class.py +++ /dev/null @@ -1,40 +0,0 @@ -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) + '>' \ No newline at end of file diff --git a/lab/sol-lab08/ok b/lab/sol-lab08/ok deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/lab/sol-lab08/ok and /dev/null differ diff --git a/lab/sol-lab08/tests/link.py b/lab/sol-lab08/tests/link.py deleted file mode 100644 index 5e35e4989c..0000000000 --- a/lab/sol-lab08/tests/link.py +++ /dev/null @@ -1,65 +0,0 @@ -test = { - 'name': 'Link', - 'points': 0, - 'suites': [ - { - 'type': 'wwpp', - 'cases': [ - { - 'code': """ - >>> from lab08 import * - >>> link = Link(1000) - >>> link.first - 1000 - >>> link.rest is Link.empty - True - >>> link = Link(1000, 2000) - Error - >>> link = Link(1000, Link()) - Error - """, - }, - { - 'code': """ - >>> from lab08 import * - >>> link = Link(1, Link(2, Link(3))) - >>> link.first - 1 - >>> link.rest.first - 2 - >>> link.rest.rest.rest is Link.empty - True - >>> link.first = 9001 - >>> link.first - 9001 - >>> link.rest = link.rest.rest - >>> link.rest.first - 3 - >>> link = Link(1) - >>> link.rest = link - >>> link.rest.rest is Link.empty - False - >>> link.rest.rest.rest.rest.first - 1 - >>> link = Link(2, Link(3, Link(4))) - >>> link2 = Link(1, link) - >>> link2.first - 1 - >>> link2.rest.first - 2 - """, - }, - { - 'code': """ - >>> from lab08 import * - >>> link = Link(5, Link(6, Link(7))) - >>> link # Look at the __repr__ method of Link - Link(5, Link(6, Link(7))) - >>> print(link) # Look at the __str__ method of Link - <5 6 7> - """, - }, - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab08/tests/trees-wwpd.py b/lab/sol-lab08/tests/trees-wwpd.py deleted file mode 100644 index b1a96660b6..0000000000 --- a/lab/sol-lab08/tests/trees-wwpd.py +++ /dev/null @@ -1,34 +0,0 @@ -test = { - 'name': 'Trees', - 'points': 0, - 'suites': [ - { - 'type': 'wwpp', - 'cases': [ - { - 'code': """ - >>> t = Tree(1, Tree(2)) # Enter Function if you believe the answer is , Error if it errors, and Nothing if nothing is displayed. - Error - >>> t = Tree(1, [Tree(2)]) - >>> t.label - 1 - >>> t.branches[0] - Tree(2) - >>> t.branches[0].label - 2 - >>> t.label = t.branches[0].label - >>> t - Tree(2, [Tree(2)]) - >>> t.branches.append(Tree(4, [Tree(8)])) - >>> len(t.branches) - 2 - >>> t.branches[0] - Tree(2) - >>> t.branches[1] - Tree(4, [Tree(8)]) - """, - }, - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab08/tree.py b/lab/sol-lab08/tree.py deleted file mode 100644 index c4e336dcf0..0000000000 --- a/lab/sol-lab08/tree.py +++ /dev/null @@ -1,33 +0,0 @@ -class Tree: - """ - >>> t = Tree(3, [Tree(2, [Tree(5)]), Tree(4)]) - >>> t.label - 3 - >>> t.branches[0].label - 2 - >>> t.branches[1].is_leaf() - True - """ - def __init__(self, label, branches=[]): - for b in branches: - assert isinstance(b, Tree) - self.label = label - self.branches = list(branches) - - def is_leaf(self): - return not self.branches - - def __repr__(self): - if self.branches: - branch_str = ', ' + repr(self.branches) - else: - branch_str = '' - return 'Tree({0}{1})'.format(self.label, branch_str) - - def __str__(self): - def print_tree(t, indent=0): - tree_str = ' ' * indent + str(t.label) + "\n" - for b in t.branches: - tree_str += print_tree(b, indent + 1) - return tree_str - return print_tree(self).rstrip() \ No newline at end of file diff --git a/lab/sol-lab09/assets/after.png b/lab/sol-lab09/assets/after.png deleted file mode 100644 index cf8e95e510..0000000000 Binary files a/lab/sol-lab09/assets/after.png and /dev/null differ diff --git a/lab/sol-lab09/assets/before.png b/lab/sol-lab09/assets/before.png deleted file mode 100644 index 6da4249e85..0000000000 Binary files a/lab/sol-lab09/assets/before.png and /dev/null differ diff --git a/lab/sol-lab09/index.html b/lab/sol-lab09/index.html deleted file mode 100644 index d273d71e0c..0000000000 --- a/lab/sol-lab09/index.html +++ /dev/null @@ -1,847 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Lab 9 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Lab 9 Solutions - - - - - - -

-
- -

Solution Files

- - - -

Scheme Introduction

- -

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.

- - - - -

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:

- - - - -

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.

- - -

YouTube link

-
- -

Consult the drop-downs below if you need a refresher on Scheme. It's -okay to skip directly to the questions and refer back -here should you get stuck.

- - - -
- -

Atomic expressions (also called atoms) are expressions without -sub-expressions, such as numbers, boolean values, and symbols.

- -
scm> 1234    ; integer
-1234
-scm> 123.4   ; real number
-123.4
-scm> #f      ; the Scheme equivalent of False in Python
-#f
- - - - - -

A Scheme symbol is equivalent to a Python name. A symbol evaluates to the value bound to that symbol in the current environment. (They are called symbols rather than names because they include + and other arithmetic symbols.)

- -
scm> quotient      ; A symbol bound to a built-in procedure
-#[quotient]
-scm> +             ; A symbol bound to a built-in procedure
-#[+]
- - - - - -

In Scheme, all values except #f (equivalent to False in Python) are true -values (unlike Python, which has other false values, such as 0).

- -
scm> #t
-#t
-scm> #f
-#f
- - -
- - - -
- -

Scheme uses Polish prefix notation, in which the operator expression comes before -the operand expressions. For example, to evaluate 3 * (4 + 2), we write:

- -
scm> (* 3 (+ 4 2))
-18
- - - -

Just like in Python, to evaluate a call expression:

- -
    -
  1. Evaluate the operator. It should evaluate to a procedure.
  2. -
  3. Evaluate the operands, left to right.
  4. -
  5. Apply the procedure to the evaluated operands.
  6. -
- -

Here are some examples using built-in procedures:

- -
scm> (+ 1 2)
-3
-scm> (- 10 (/ 6 2))
-7
-scm> (modulo 35 4)
-3
-scm> (even? (quotient 45 2))
-#t
- - -
- - - -
- -

Define: -The define form is used to assign values to symbols. It has the following syntax:

- -
(define <symbol> <expression>)
- -
scm> (define pi (+ 3 0.14))
-pi
-scm> pi
-3.14
- - - -

To evaluate the define expression:

- -
    -
  1. Evaluate the final sub-expression (<expression>), which in this case evaluates to 3.14.
  2. -
  3. Bind that value to the symbol (symbol), which in this case is pi.
  4. -
  5. Return the symbol.
  6. -
- -

The define form can also define new procedures, described in the "Defining Functions" section. -

- -

If Expressions: -The if special form evaluates one of two expressions based on a predicate.

- -
(if <predicate> <if-true> <if-false>)
- -

The rules for evaluating an if special form expression are as follows:

- -
    -
  1. Evaluate the <predicate>.
  2. -
  3. If the <predicate> evaluates to a true value (anything but #f), evaluate - and return the value of the <if-true> expression. Otherwise, evaluate and - return the value of the <if-false> expression.
  4. -
- -

For example, this expression does not error and evaluates to 5, even though the -sub-expression (/ 1 (- x 3)) would error if evaluated.

- -
scm> (define x 3)
-x
-scm> (if (> (- x 3) 0) (/ 1 (- x 3)) (+ x 2))
-5
- - - -

The <if-false> expression is optional.

- -
scm> (if (= x 3) (print x))
-3
- - - -

Let's compare a Scheme if expression with a Python if statement:

- -
    -
  • In Scheme:
  • -
- -
    (if (> x 3) 1 2)
- - - -
    -
  • In Python:
  • -
- -
    if x > 3:
-        1
-    else:
-        2
- - - -

The Scheme if expression evaluates to a number (either 1 or 2, depending on -x). The Python statement does not evaluate to anything, and so the 1 and 2 -cannot be used or accessed.

- -

Another difference between the two is that it's possible to add more lines of -code into the suites of the Python if statement, while a Scheme if -expression expects just a single expression in each of the <if-true> and -<if-false> positions.

- -

One final difference is that in Scheme, you cannot write elif clauses. -

- -

Cond Expressions: -The cond special form can include multiple predicates (like if/elif in Python):

- -
(cond
-    (<p1> <e1>)
-    (<p2> <e2>)
-    ...
-    (<pn> <en>)
-    (else <else-expression>))
- -

The first expression in each clause is a predicate. The second expression in -the clause is the return expression corresponding to its predicate. The else -clause is optional; its <else-expression> is the return expression if none of -the predicates are true.

- -

The rules of evaluation are as follows:

- -
    -
  1. Evaluate the predicates <p1>, <p2>, ..., <pn> in order until one - evaluates to a true value (anything but #f).
  2. -
  3. Evalaute and return the value of the return expression corresponding to the - first predicate expression with a true value.
  4. -
  5. If none of the predicates evaluate to true values and there is an else - clause, evaluate and return <else-expression>.
  6. -
- -

For example, this cond expression returns the nearest multiple of 3 to x:

- -
scm> (define x 5)
-x
-scm> (cond ((= (modulo x 3) 0) x)
-            ((= (modulo x 3) 1) (- x 1))
-            ((= (modulo x 3) 2) (+ x 1)))
-6
- - -

- -

Lambdas: -The lambda special form creates a procedure.

- -
(lambda (<param1> <param2> ...) <body>)
- - - -

This expression will create and return a procedure with the given formal -parameters and body, similar to a lambda expression in Python.

- -
scm> (lambda (x y) (+ x y))        ; Returns a lambda procedure, but doesn't assign it to a name
-(lambda (x y) (+ x y))
-scm> ((lambda (x y) (+ x y)) 3 4)  ; Create and call a lambda procedure in one line
-7
- - - -

Here are equivalent expressions in Python:

- -
>>> lambda x, y: x + y
-<function <lambda> at ...>
->>> (lambda x, y: x + y)(3, 4)
-7
- - - -

The <body> may contain multiple expressions. A scheme procedure returns the -value of the last expression in its body.

- -
- - - -
- -

The define form can create a procedure and give it a name:

- -
(define (<symbol> <param1> <param2> ...) <body>)
- -

For example, this is how we would define the double procedure:

- -
scm> (define (double x) (* x 2))
-double
-scm> (double 3)
-6
- - - -

Here's an example with three arguments:

- -
scm> (define (add-then-mul x y z)
-        (* (+ x y) z))
-scm> (add-then-mul 3 4 5)
-35
- - - -

When a define expression is evaluated, the following occurs:

- -
    -
  1. Create a procedure with the given parameters and <body>.
  2. -
  3. Bind the procedure to the <symbol> in the current frame.
  4. -
  5. Return the <symbol>.
  6. -
- - - -

The following two expressions are equivalent:

- -
scm> (define add (lambda (x y) (+ x y)))
-add
-scm> (define (add x y) (+ x y))
-add
- - -
- - -

Q1: Over or Under

- - -

Define a procedure over-or-under which takes in a number num1 and a number num2 -and returns the following:

- -
    -
  • -1 if num1 is less than num2
  • -
  • 0 if num1 is equal to num2
  • -
  • 1 if num1 is greater than num2
  • -
- -

NOTE. -Remember that every parenthesis in Scheme makes a function call. For example, just typing 0 in the Scheme interpeter will return 0. However, typing (0) will cause an Error because 0 is not a function.

- -

Challenge: Implement this in 2 different ways using if and cond!

- - - -
(define (over-or-under num1 num2)
-
(cond - ((< num1 num2) -1) - ((= num1 num2) 0) - (else 1)) -
)
- - - -
- -

Use Ok to test your code:

python3 ok -q over_or_under
- -
- - -

Q2: Make Adder

- - -

Write the procedure make-adder which takes in an initial number, -num, and then returns a procedure. This returned procedure takes in a -number inc and returns the result of num + inc.

- -

Hint: To return a procedure, you can either return a lambda expression -or define another nested procedure.

- -

Note: define doesn't return the function, but lambda does.

- -

Hint: Scheme will automatically return the last clause in your procedure.

- -

You can find documentation on the syntax of lambda expressions in -the 61A scheme specification!

- - - -
(define (make-adder num)
-
(lambda (inc) (+ inc num)) -
)
- - -
- -

Use Ok to test your code:

python3 ok -q make_adder
- -
- - -

Q3: Compose

- - -

Write the procedure composed, which takes in procedures f and g -and outputs a new procedure. This new procedure takes in a number x -and outputs the result of calling f on g of x.

- -

NOTE. -Remember to use Scheme syntax when calling functions. That is, in the form of (func arg), not func(arg).

- - - -
(define (composed f g)
-
(lambda (x) (f (g x)))
)
- - -
- -

Use Ok to test your code:

python3 ok -q composed
- -
- - -

Q4: Repeat

- - -

Write the procedure repeat, which takes in a procedure f and a number n, and outputs a new procedure. This new procedure takes in a number x and outputs the result of applying f to x a total of n times. For example:

- -
scm> (define (square x) (* x x))
-square
-scm> ((repeat square 2) 5) ; (square (square 5))
-625
-scm> ((repeat square 3) 3) ; (square (square (square 3)))
-6561
-scm> ((repeat square 1) 7) ; (square 7)
-49
- - - -

Hint: The composed function you wrote in the previous problem might be useful.

- - - -
(define (repeat f n)
-
; note: this relies on `composed` being implemented correctly - (if (< n 1) - (lambda (x) x) - (composed f (repeat f (- n 1))))
)
- - -
- -

Use Ok to test your code:

python3 ok -q repeat
- -
- - -

Q5: Greatest Common Divisor

- - -

The GCD is the the greatest common divisor of two positive integers.

- -

Write the procedure gcd, which computes the GCD of numbers a and b using -Euclid's algorithm, which uses the fact that the GCD of two values is either of -the following:

- -
    -
  • the smaller value if it evenly divides the larger value, or
  • -
  • the greatest common divisor of the smaller value and the remainder of - the larger value divided by the smaller value
  • -
- -

In other words, if a is greater than b and a is not divisible by -b, then

- -
gcd(a, b) = gcd(b, a % b)
- -

You may find the provided procedures min and max helpful. You can also use -the built-in modulo and zero? procedures.

- -
scm> (modulo 10 4)
-2
-scm> (zero? (- 3 3))
-#t
-scm> (zero? 3)
-#f
- - - -
(define (max a b) (if (> a b) a b))
-(define (min a b) (if (> a b) b a))
-(define (gcd a b)
-
(cond ((zero? a) b) - ((zero? b) a) - ((= (modulo (max a b) (min a b)) 0) (min a b)) - (else (gcd (min a b) (modulo (max a b) (min a b)))))
)
- - -
- -

Use Ok to test your code:

python3 ok -q gcd
- -
- - -

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.

- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/lab/sol-lab09/lab09.ok b/lab/sol-lab09/lab09.ok deleted file mode 100644 index f7c8942b68..0000000000 --- a/lab/sol-lab09/lab09.ok +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "Lab 9", - "endpoint": "cal/cs61a/fa24/lab09", - "src": [ - "lab09.scm" - ], - "tests": { - "tests/*.py": "ok_test" - }, - "default_tests": [ - "over_or_under", - "make_adder", - "composed", - "repeat", - "gcd" - ], - "protocols": [ - "restore", - "file_contents", - "unlock", - "grading", - "analytics", - "backup" - ] -} \ No newline at end of file diff --git a/lab/sol-lab09/lab09.scm b/lab/sol-lab09/lab09.scm deleted file mode 100644 index b8a3e610cf..0000000000 --- a/lab/sol-lab09/lab09.scm +++ /dev/null @@ -1,37 +0,0 @@ -(define (over-or-under num1 num2) - (cond - ((< num1 num2) -1) - ((= num1 num2) 0) - (else 1))) - -(define (make-adder num) - (lambda (inc) (+ inc num))) - -(define (composed f g) (lambda (x) (f (g x)))) - -(define (repeat f n) - ; note: this relies on `composed` being implemented correctly - (if (< n 1) - (lambda (x) x) - (composed f (repeat f (- n 1))))) - -(define (max a b) - (if (> a b) - a - b)) - -(define (min a b) - (if (> a b) - b - a)) - -(define (gcd a b) - (cond - ((zero? a) - b) - ((zero? b) - a) - ((= (modulo (max a b) (min a b)) 0) - (min a b)) - (else - (gcd (min a b) (modulo (max a b) (min a b)))))) diff --git a/lab/sol-lab09/lab09.zip b/lab/sol-lab09/lab09.zip deleted file mode 100644 index 3f416b2140..0000000000 Binary files a/lab/sol-lab09/lab09.zip and /dev/null differ diff --git a/lab/sol-lab09/ok b/lab/sol-lab09/ok deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/lab/sol-lab09/ok and /dev/null differ diff --git a/lab/sol-lab09/scheme b/lab/sol-lab09/scheme deleted file mode 100644 index 2174145cff..0000000000 Binary files a/lab/sol-lab09/scheme and /dev/null differ diff --git a/lab/sol-lab09/tests/combinations.py b/lab/sol-lab09/tests/combinations.py deleted file mode 100644 index 3d75caafba..0000000000 --- a/lab/sol-lab09/tests/combinations.py +++ /dev/null @@ -1,76 +0,0 @@ -test = { - 'name': 'What Would Scheme Display?', - 'points': 0, - 'suites': [ - { - 'type': 'scheme', - 'scored': True, - 'setup': """ - """, - 'cases': [ - { - 'code':""" - scm> (- 10 4) - 6 - scm> (* 7 6) - 42 - scm> (+ 1 2 3 4) - 10 - scm> (/ 8 2 2) - 2 - scm> (quotient 29 5) - 5 - scm> (modulo 29 5) - 4 - """, - }, - { - 'code': """ - scm> (= 1 3) ; Scheme uses '=' instead of '==' for comparison - #f - scm> (< 1 3) - #t - scm> (or 1 #t) ; or special form short circuits - 1 - scm> (and #t #f (/ 1 0)) - #f - scm> (not #t) - #f - """, - }, - { - 'code': """ - scm> (define x 3) - x - scm> x - 3 - scm> (define y (+ x 4)) - y - scm> y - 7 - scm> (define x (lambda (y) (* y 2))) - x - scm> (x y) - 14 - """, - }, - { - 'code': """ - scm> (if (not (print 1)) (print 2) (print 3)) - 1 - 3 - scm> (* (if (> 3 2) 1 2) (+ 4 5)) - 9 - scm> (define foo (lambda (x y z) (if x y z))) - foo - scm> (foo 1 2 (print 'hi)) - hi - 2 - scm> ((lambda (a) (print 'a)) 100) - a - """, - } - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab09/tests/composed.py b/lab/sol-lab09/tests/composed.py deleted file mode 100644 index 6ad1a71481..0000000000 --- a/lab/sol-lab09/tests/composed.py +++ /dev/null @@ -1,74 +0,0 @@ -test = { - 'name': 'composed', - 'points': 1, - 'suites': [ - { - 'scored': True, - 'setup': """ - scm> (load-all ".") - scm> (define (add-one a) (+ a 1)) - scm> (define (multiply-by-two a) (* a 2)) - scm> (define add-then-mul (composed multiply-by-two add-one)) - """, - 'type': 'scheme', - 'cases': [ - { - 'code': """ - scm> (add-then-mul 2) - 6 - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> ((composed add-one add-one) 2) - 4 - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> ((composed multiply-by-two multiply-by-two) 2) - 8 - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> ((composed add-one multiply-by-two) 2) - 5 - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> ((composed (composed add-one add-one) add-one) 2) - 5 - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> ((composed (composed add-one add-one) multiply-by-two) 2) - 6 - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> ((composed multiply-by-two (composed add-one add-one)) 2) - 8 - """, - 'hidden': False, - 'locked': False - } - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab09/tests/deep-map.py b/lab/sol-lab09/tests/deep-map.py deleted file mode 100644 index c72592b38c..0000000000 --- a/lab/sol-lab09/tests/deep-map.py +++ /dev/null @@ -1,27 +0,0 @@ -test = { - 'name': 'deep-map', - 'points': 1, - 'suites': [ - { - 'type': 'scheme', - 'cases': [ - { - 'locked': False, - 'code': r""" - scm> (define (double x) (* 2 x)) - double - scm> (deep-map double '(1 2 3 4)) - (2 4 6 8) - scm> (deep-map double '(2 (3 4))) - (4 (6 8)) - scm> (deep-map double '(1 2 3 (4) 5)) - (2 4 6 (8) 10) - """, - }, - ], - 'setup': r""" - scm> (load-all ".") - """, - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab09/tests/gcd.py b/lab/sol-lab09/tests/gcd.py deleted file mode 100644 index df47113944..0000000000 --- a/lab/sol-lab09/tests/gcd.py +++ /dev/null @@ -1,31 +0,0 @@ -test = { - 'name': 'gcd', - 'points': 1, - 'suites': [ - { - 'scored': True, - 'setup': """ - scm> (load-all ".") - """, - 'type': 'scheme', - 'cases': [ - { - 'code': """ - scm> (gcd 24 60) - 12 - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> (gcd 1071 462) - 21 - """, - 'hidden': False, - 'locked': False - } - ], - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab09/tests/list_duplicate.py b/lab/sol-lab09/tests/list_duplicate.py deleted file mode 100644 index 49f008c22e..0000000000 --- a/lab/sol-lab09/tests/list_duplicate.py +++ /dev/null @@ -1,47 +0,0 @@ -test = { - 'name': 'duplicate', - 'points': 1, - 'suites': [ - { - 'type': 'scheme', - 'scored': True, - 'setup': """ - scm> (load-all ".") - """, - 'cases': [ - { - 'code': """ - scm> (duplicate '()) - () - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> (duplicate '(1 2 3)) - (1 1 2 2 3 3) - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> (duplicate '(1)) - (1 1) - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> (duplicate '(0)) - (0 0) - """, - 'hidden': False, - 'locked': False - } - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab09/tests/make_adder.py b/lab/sol-lab09/tests/make_adder.py deleted file mode 100644 index 9b85f5f291..0000000000 --- a/lab/sol-lab09/tests/make_adder.py +++ /dev/null @@ -1,49 +0,0 @@ -test = { - 'name': 'make-adder', - 'points': 1, - 'suites': [ - { - 'scored': True, - 'setup': """ - scm> (load-all ".") - scm> (define add-two (make-adder 2)) - scm> (define add-three (make-adder 3)) - """, - 'type': 'scheme', - 'cases': [ - { - 'code': """ - scm> (add-two 2) - 4 - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> (add-two 3) - 5 - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> (add-three 3) - 6 - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> (add-three 9) - 12 - """, - 'hidden': False, - 'locked': False - } - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab09/tests/over_or_under.py b/lab/sol-lab09/tests/over_or_under.py deleted file mode 100644 index 9115a3999f..0000000000 --- a/lab/sol-lab09/tests/over_or_under.py +++ /dev/null @@ -1,39 +0,0 @@ -test = { - 'name': 'over-or-under', - 'points': 1, - 'suites': [ - { - 'type': 'scheme', - 'scored': True, - 'setup': """ - scm> (load-all ".") - """, - 'cases': [ - { - 'code': """ - scm> (over-or-under 5 5) - 0 - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> (over-or-under 5 4) - 1 - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> (over-or-under 3 5) - -1 - """, - 'hidden': False, - 'locked': False - } - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab09/tests/repeat.py b/lab/sol-lab09/tests/repeat.py deleted file mode 100644 index 1da5c90275..0000000000 --- a/lab/sol-lab09/tests/repeat.py +++ /dev/null @@ -1,41 +0,0 @@ -test = { - 'name': 'repeat', - 'points': 1, - 'suites': [ - { - 'scored': True, - 'setup': """ - scm> (load-all ".") - """, - 'type': 'scheme', - 'cases': [ - { - 'code': """ - scm> (define (square x) (* x x)) - square - scm> ((repeat square 2) 5) ; (square (square 5)) - 625 - scm> ((repeat square 3) 3) ; (square (square (square 3))) - 6561 - scm> ((repeat square 1) 7) ; (square 7) - 49 - """, - 'hidden': False, - 'locked': False - }, - { - 'code': """ - scm> (define (increment x) (+ x 1)) - increment - scm> ((repeat increment 4) 2) ; (increment (increment (increment (increment 2)))) - 6 - scm> ((repeat increment 10) 51) - 61 - """, - 'hidden': False, - 'locked': False - }, - ], - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab10/assets/parser.png b/lab/sol-lab10/assets/parser.png deleted file mode 100644 index a18434c90b..0000000000 Binary files a/lab/sol-lab10/assets/parser.png and /dev/null differ diff --git a/lab/sol-lab10/assets/repl.png b/lab/sol-lab10/assets/repl.png deleted file mode 100644 index 280e3b0b83..0000000000 Binary files a/lab/sol-lab10/assets/repl.png and /dev/null differ diff --git a/lab/sol-lab10/assets/to_pair.png b/lab/sol-lab10/assets/to_pair.png deleted file mode 100644 index 512b3a0af3..0000000000 Binary files a/lab/sol-lab10/assets/to_pair.png and /dev/null differ diff --git a/lab/sol-lab10/index.html b/lab/sol-lab10/index.html deleted file mode 100644 index a78c087a6f..0000000000 --- a/lab/sol-lab10/index.html +++ /dev/null @@ -1,747 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Lab 10 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Lab 10 Solutions - - - - - - -

-
- -

Solution Files

- - - -

Topics

- - -

Consult this section if you need a refresher on the material for this lab. It's -okay to skip directly to the questions and refer back -here should you get stuck.

- -
- - -
- - -

Interpreters

- - -

An interpreter is a program that allows you to interact with the -computer using a specific language. It takes the code you write, -interprets it, and then executes the corresponding actions, often -using a more fundamental language to communicate with the computer -hardware.

- -

In Project 4, you'll develop an interpreter for the Scheme -programming language using Python. Interestingly, the Python -interpreter you've been using throughout this course is primarily -written in the C programming language. At the lowest level, computers -operate by interpreting machine code, which is a series of ones and -zeros that instructs the computer on performing basic tasks such as -arithmetic operations and data retrieval.

- -

When we talk about an interpreter, there are two languages at work:

- -
    -
  1. The language being interpreted: For Project 4, this is the Scheme language.
  2. -
  3. The implementation language: This is the language used to create - the interpreter itself, which, for Project 4, will be Python.
  4. -
- -
- -

REPL

- -

A common feature of interpreters is the Read-Eval-Print Loop (REPL), which processes user inputs in a cyclic fashion through three stages:

- -
    -
  • Read: The interpreter first reads the input string provided by the user. - This input goes through a parsing process that involves two key steps:

    - -
      -
    • The lexical analysis step breaks down the input string into tokens, - which are the basic elements or "words" of the language you're - interpreting. These tokens represent the smallest units of meaning within the input.
    • -
    • The syntactic analysis step takes the tokens from the previous step and organizes - them into a data structure that the underlying language can understand. For our - Scheme interpreter, we assemble the tokens into a Pair object (similar to a - Link), to represent the structure of the original call expression.

      - -
        -
      • The first item in the Pair represents the operator of the call expression, while the subsequent elements are the operands or arguments upon which the operation will act. Note that these operands can also be call expressions themselves (nested expressions). -
      • -
    • -
  • -
- -

Below is a summary of the read process for a Scheme expression input:

- -


- -

- -
    -
  • Eval: This step evaluates the expressions you've written in that programming - language to obtain a value. It involves the following two functions:

    - -
      -
    • eval takes an expression and evaluates it based on the language's rules. - When the expression is a call expression, eval uses the apply function to obtain the result. - It will evaluate the operator and its operands in order. For example, in (add 1 2), - eval would identify add as the operator and 1 and 2 as the operands. - It evaluates add to ensure it's a valid function and then evaluates 1 and - 2 to ensure they're valid arguments.
    • -
    • apply takes the evaluated operator (the function) and applies it to the - evaluated operands (the arguments). Note that it's possible that, during this process, apply needs to evaluate more expressions (like those found within the function body). This is where apply may call back to eval, and thus these two stages are mutually recursive.
    • -
  • -
  • Print: Display the result of evaluating the user input.
  • -
- -
-Here's how all the pieces fit together:
- -

- -
-
- - -

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.

- - -

YouTube link

-
- - -

Calculator

- - -

An interpreter is a program that executes programs. Today, we will extend the interpreter for Calculator, a simple made-up language that is a subset of Scheme. This lab is like Project 4 in miniature.

- -

The Calculator language includes only the four basic arithmetic operations: +, -, *, and /. These operations can be nested and can take various numbers of arguments, just like in Scheme. A few examples of calculator expressions and their corresponding values are shown below.

- -
 calc> (+ 2 2 2)
- 6
-
- calc> (- 5)
- -5
-
- calc> (* (+ 1 2) (+ 2 3 4))
- 27
- - - -

Calculator expressions are represented as Python objects:

- -
    -
  • Numbers are represented using Python numbers.
  • -
  • The symbols for arithmetic operations are represented using Python strings (e.g. '+').
  • -
  • Call expressions are represented using the Pair class below.
  • -
- - -

Pair Class

- - -

To represent Scheme lists in Python, we will use the Pair class (in both this lab and the Scheme project). A Pair instance has two attributes: first and rest. Pair is always called on two arguments. To make a list, nest calls to Pair and pass in nil as the second argument of the last pair.

- -
    -
  • Look familiar? Pair is very similar to Link, the class we used to represent linked lists. They differ in their str representation: printing a Pair instance displays the list using Scheme syntax.
  • -
- -

Note In the Python code, nil is bound to a user-defined object that represents an empty Scheme list. -Similarly, nil in Scheme evaluates to an empty list.

- -

For example, once our interpreter reads in the Scheme expression (+ 2 3), it is represented as Pair('+', Pair(2, Pair(3, nil))).

- -
>>> p = Pair('+', Pair(2, Pair(3, nil)))
->>> p.first
-'+'
->>> p.rest
-Pair(2, Pair(3, nil))
->>> p.rest.first
-2
->>> print(p)
-(+ 2 3)
- - - -

The Pair class has a map method that takes a one-argument python function fn. It returns the Scheme list that results from applying fn to each element of the Scheme list.

- -
>>> p.rest.map(lambda x: 2 * x)
-Pair(4, Pair(6, nil))
- - - -
- - -
- -

Here is the Pair class and nil object (__str__ and __repr__ methods not -shown).

- -
class Pair:
-    """Represents the built-in pair data structure in Scheme."""
-    def __init__(self, first, rest):
-        self.first = first
-        self.rest = rest
-
-    def map(self, fn):
-        """Return a Scheme list after mapping Python function FN to SELF."""
-        mapped = fn(self.first)
-        if self.rest is nil or isinstance(self.rest, Pair):
-            return Pair(mapped, self.rest.map(fn))
-        else:
-            raise TypeError('ill-formed list')
- - - -
class nil:
-    """The empty list"""
-
-    def map(self, fn):
-        return self
-
-nil = nil() # Assignment hides the nil class; there is only one instance
- - - -
-
- - - - -

Q1: Using Pair

- - -

Answer the following questions about a Pair instance -representing the Calculator expression (+ (- 2 4) 6 8).

- -

Use Ok to test your understanding:

python3 ok -q using_pair -u
- -
- - -

Calculator Evaluation

- - -

For Question 2 (New Procedure) and Question 4 (Saving Values), you'll need to update the calc_eval function below, which evaluates a Calculator expression. For Question 2, you'll determine what are the operator and operands for a call expression in Scheme as well as how to apply a procedure to arguments the calc_apply line. For Question 4, you'll determine how to look up the value of symbols previously defined.

- - - -
def calc_eval(exp):
-    """
-    >>> calc_eval(Pair("define", Pair("a", Pair(1, nil))))
-    'a'
-    >>> calc_eval("a")
-    1
-    >>> calc_eval(Pair("+", Pair(1, Pair(2, nil))))
-    3
-    """
-    if isinstance(exp, Pair):
-
operator = exp.first
operands = exp.rest
if operator == 'and': # and expressions - return eval_and(operands) - elif operator == 'define': # define expressions - return eval_define(operands) - else: # Call expressions -
return calc_apply(calc_eval(operator), operands.map(calc_eval))
elif exp in OPERATORS: # Looking up procedures - return OPERATORS[exp] - elif isinstance(exp, int) or isinstance(exp, bool): # Numbers and booleans - return exp -
elif exp in bindings: # Looking up variables
return bindings[exp]
- -
- - -

Q2: New Procedure

- - -

Add the // operation to Calculator, a floor-division procedure such that (// dividend divisor) returns the result of dividing dividend by divisor, ignoring the remainder (dividend // divisor in Python). Handle multiple inputs as illustrated in the following example: (// dividend divisor1 divisor2 divisor3) evaluates to (((dividend // divisor1) // divisor2) // divisor3) in Python. Assume every call to // has at least two arguments.

- -

Hint: You will need to modify both the calc_eval and floor_div methods for this question!

- -
calc> (// 1 1)
-1
-calc> (// 5 2)
-2
-calc> (// 28 (+ 1 1) 1)
-14
- - - -

Hint: Make sure that every element in a Pair (the operator and all operands) will be calc_eval-uated once, so that we can correctly apply the relevant Python operator to operands! You may find the map method of the Pair class useful for this.

- - - -
def floor_div(args):
-    """
-    >>> floor_div(Pair(100, Pair(10, nil)))
-    10
-    >>> floor_div(Pair(5, Pair(3, nil)))
-    1
-    >>> floor_div(Pair(1, Pair(1, nil)))
-    1
-    >>> floor_div(Pair(5, Pair(2, nil)))
-    2
-    >>> floor_div(Pair(23, Pair(2, Pair(5, nil))))
-    2
-    >>> calc_eval(Pair("//", Pair(4, Pair(2, nil))))
-    2
-    >>> calc_eval(Pair("//", Pair(100, Pair(2, Pair(2, Pair(2, Pair(2, Pair(2, nil))))))))
-    3
-    >>> calc_eval(Pair("//", Pair(100, Pair(Pair("+", Pair(2, Pair(3, nil))), nil))))
-    20
-    """
-
result = args.first - divisors = args.rest - while divisors != nil: - divisor = divisors.first - result //= divisor - divisors = divisors.rest - return result
- -
- -

Use Ok to test your code:

python3 ok -q floor_div
- -
- - -

Q3: New Form

- - -

Add and expressions to our -Calculator interpreter as well as introduce the Scheme boolean values -#t and #f, represented as Python True and False. -(The examples below assumes conditional operators (e.g. <, >, =, etc) have already been implemented, -but you do not have to worry about them for this question.)

- -
calc> (and (= 1 1) 3)
-3
-calc> (and (+ 1 0) (< 1 0) (/ 1 0))
-#f
-calc> (and #f (+ 1 0))
-#f
-calc> (and 0 1 (+ 5 1)) ; 0 is a true value in Scheme!
-6
- - - -

In a call expression, we first evaluate the operator, then evaluate the operands, and finally apply the procedure to its arguments (just like you did for floor_div in the previous question). -However, since and is a special form that short circuits on the first false argument, we cannot evaluate and expressions the same way we evaluate call expressions. We need to add special logic for forms that don't always evaluate all the sub-expressions.

- -

Important: To check whether some val is a false value in Scheme, use -val is scheme_f rather than val == scheme_f.

- - - -
scheme_t = True   # Scheme's #t
-scheme_f = False  # Scheme's #f
-
-def eval_and(expressions):
-    """
-    >>> calc_eval(Pair("and", Pair(1, nil)))
-    1
-    >>> calc_eval(Pair("and", Pair(False, Pair("1", nil))))
-    False
-    >>> calc_eval(Pair("and", Pair(1, Pair(Pair("//", Pair(5, Pair(2, nil))), nil))))
-    2
-    >>> calc_eval(Pair("and", Pair(Pair('+', Pair(1, Pair(1, nil))), Pair(3, nil))))
-    3
-    >>> calc_eval(Pair("and", Pair(Pair('-', Pair(1, Pair(0, nil))), Pair(Pair('/', Pair(5, Pair(2, nil))), nil))))
-    2.5
-    >>> calc_eval(Pair("and", Pair(0, Pair(1, nil))))
-    1
-    >>> calc_eval(Pair("and", nil))
-    True
-    """
-
curr, val = expressions, True - while curr is not nil: - val = calc_eval(curr.first) - if val is scheme_f: - return scheme_f - curr = curr.rest - return val
- -
- -

Use Ok to test your code:

python3 ok -q eval_and
- -
- - -

Q4: Saving Values

- - -

Implement a define special form that binds values to symbols. This should work like define in Scheme: (define <symbol> <expression>) first evaluates the expression, then binds the symbol to its value. The whole define expression evaluates to the symbol.

- -
calc> (define a 1)
-a
-calc> a
-1
- - - -

This is a more involved change. Here are the 4 steps involved:

- -
    -
  1. Add a bindings dictionary that will store the symbols and correspondings values (done for you).
  2. -
  3. Identify when the define form is given to calc_eval (done for you).
  4. -
  5. Allow symbols bound to values to be looked up in calc_eval.
  6. -
  7. Write the function eval_define which should add symbols and values to the bindings dictionary.
  8. -
- - - -
bindings = {}
-
-def eval_define(expressions):
-    """
-    >>> eval_define(Pair("a", Pair(1, nil)))
-    'a'
-    >>> eval_define(Pair("b", Pair(3, nil)))
-    'b'
-    >>> eval_define(Pair("c", Pair("a", nil)))
-    'c'
-    >>> calc_eval("c")
-    1
-    >>> calc_eval(Pair("define", Pair("d", Pair("//", nil))))
-    'd'
-    >>> calc_eval(Pair("d", Pair(4, Pair(2, nil))))
-    2
-    """
-
symbol, value = expressions.first, calc_eval(expressions.rest.first) - bindings[symbol] = value - return symbol
- -
- -

Use Ok to test your code:

python3 ok -q eval_define
- -
- - -

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.

- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/lab/sol-lab10/lab10.ok b/lab/sol-lab10/lab10.ok deleted file mode 100644 index 57fb7b8ef9..0000000000 --- a/lab/sol-lab10/lab10.ok +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "Lab 10", - "endpoint": "cal/cs61a/fa24/lab10", - "src": [ - "lab10.py" - ], - "tests": { - "lab*.py": "doctest", - "tests/*.py": "ok_test" - }, - "default_tests": [ - "using_pair", - "floor_div", - "eval_and", - "eval_define" - ], - "protocols": [ - "restore", - "file_contents", - "unlock", - "grading", - "analytics", - "backup" - ] -} \ No newline at end of file diff --git a/lab/sol-lab10/lab10.py b/lab/sol-lab10/lab10.py deleted file mode 100644 index 943fdf27c8..0000000000 --- a/lab/sol-lab10/lab10.py +++ /dev/null @@ -1,179 +0,0 @@ -############## You do not need to understand any of this code! -import base64 -ob = "CmRlZiBhZGRpdGlvbihleHByKToKICAgIGRpdmlkZW5kID0gZXhwci5maXJzdAogICAgZXhwciA9IGV4cHIucmVzdAogICAgd2hpbGUgZXhwciAhPSBuaWw6CiAgICAgICAgZGl2aXNvciA9IGV4cHIuZmlyc3QKICAgICAgICBkaXZpZGVuZCArPSBkaXZpc29yCiAgICAgICAgZXhwciA9IGV4cHIucmVzdAogICAgcmV0dXJuIGRpdmlkZW5kCgpkZWYgc3VidHJhY3Rpb24oZXhwcik6CiAgICBkaXZpZGVuZCA9IGV4cHIuZmlyc3QKICAgIGV4cHIgPSBleHByLnJlc3QKICAgIHdoaWxlIGV4cHIgIT0gbmlsOgogICAgICAgIGRpdmlzb3IgPSBleHByLmZpcnN0CiAgICAgICAgZGl2aWRlbmQgLT0gZGl2aXNvcgogICAgICAgIGV4cHIgPSBleHByLnJlc3QKICAgIHJldHVybiBkaXZpZGVuZAoKZGVmIG11bHRpcGxpY2F0aW9uKGV4cHIpOgogICAgZGl2aWRlbmQgPSBleHByLmZpcnN0CiAgICBleHByID0gZXhwci5yZXN0CiAgICB3aGlsZSBleHByICE9IG5pbDoKICAgICAgICBkaXZpc29yID0gZXhwci5maXJzdAogICAgICAgIGRpdmlkZW5kICo9IGRpdmlzb3IKICAgICAgICBleHByID0gZXhwci5yZXN0CiAgICByZXR1cm4gZGl2aWRlbmQKCmRlZiBkaXZpc2lvbihleHByKToKICAgIGRpdmlkZW5kID0gZXhwci5maXJzdAogICAgZXhwciA9IGV4cHIucmVzdAogICAgd2hpbGUgZXhwciAhPSBuaWw6CiAgICAgICAgZGl2aXNvciA9IGV4cHIuZmlyc3QKICAgICAgICBkaXZpZGVuZCAvPSBkaXZpc29yCiAgICAgICAgZXhwciA9IGV4cHIucmVzdAogICAgcmV0dXJuIGRpdmlkZW5kCg==" -exec(base64.b64decode(ob.encode("ascii")).decode("ascii")) -############## - -def calc_eval(exp): - """ - >>> calc_eval(Pair("define", Pair("a", Pair(1, nil)))) - 'a' - >>> calc_eval("a") - 1 - >>> calc_eval(Pair("+", Pair(1, Pair(2, nil)))) - 3 - """ - if isinstance(exp, Pair): - operator = exp.first - operands = exp.rest - if operator == 'and': # and expressions - return eval_and(operands) - elif operator == 'define': # define expressions - return eval_define(operands) - else: # Call expressions - return calc_apply(calc_eval(operator), operands.map(calc_eval)) - elif exp in OPERATORS: # Looking up procedures - return OPERATORS[exp] - elif isinstance(exp, int) or isinstance(exp, bool): # Numbers and booleans - return exp - elif exp in bindings: # Looking up variables - return bindings[exp] - -def calc_apply(op, args): - return op(args) - -def floor_div(args): - """ - >>> floor_div(Pair(100, Pair(10, nil))) - 10 - >>> floor_div(Pair(5, Pair(3, nil))) - 1 - >>> floor_div(Pair(1, Pair(1, nil))) - 1 - >>> floor_div(Pair(5, Pair(2, nil))) - 2 - >>> floor_div(Pair(23, Pair(2, Pair(5, nil)))) - 2 - >>> calc_eval(Pair("//", Pair(4, Pair(2, nil)))) - 2 - >>> calc_eval(Pair("//", Pair(100, Pair(2, Pair(2, Pair(2, Pair(2, Pair(2, nil)))))))) - 3 - >>> calc_eval(Pair("//", Pair(100, Pair(Pair("+", Pair(2, Pair(3, nil))), nil)))) - 20 - """ - result = args.first - divisors = args.rest - while divisors != nil: - divisor = divisors.first - result //= divisor - divisors = divisors.rest - return result - -scheme_t = True # Scheme's #t -scheme_f = False # Scheme's #f - -def eval_and(expressions): - """ - >>> calc_eval(Pair("and", Pair(1, nil))) - 1 - >>> calc_eval(Pair("and", Pair(False, Pair("1", nil)))) - False - >>> calc_eval(Pair("and", Pair(1, Pair(Pair("//", Pair(5, Pair(2, nil))), nil)))) - 2 - >>> calc_eval(Pair("and", Pair(Pair('+', Pair(1, Pair(1, nil))), Pair(3, nil)))) - 3 - >>> calc_eval(Pair("and", Pair(Pair('-', Pair(1, Pair(0, nil))), Pair(Pair('/', Pair(5, Pair(2, nil))), nil)))) - 2.5 - >>> calc_eval(Pair("and", Pair(0, Pair(1, nil)))) - 1 - >>> calc_eval(Pair("and", nil)) - True - """ - curr, val = expressions, True - while curr is not nil: - val = calc_eval(curr.first) - if val is scheme_f: - return scheme_f - curr = curr.rest - return val - -bindings = {} - -def eval_define(expressions): - """ - >>> eval_define(Pair("a", Pair(1, nil))) - 'a' - >>> eval_define(Pair("b", Pair(3, nil))) - 'b' - >>> eval_define(Pair("c", Pair("a", nil))) - 'c' - >>> calc_eval("c") - 1 - >>> calc_eval(Pair("define", Pair("d", Pair("//", nil)))) - 'd' - >>> calc_eval(Pair("d", Pair(4, Pair(2, nil)))) - 2 - """ - symbol, value = expressions.first, calc_eval(expressions.rest.first) - bindings[symbol] = value - return symbol - -OPERATORS = { "//": floor_div, "+": addition, "-": subtraction, "*": multiplication, "/": division } - -class Pair: - """A pair has two instance attributes: first and rest. rest must be a Pair or nil - - >>> s = Pair(1, Pair(2, nil)) - >>> s - Pair(1, Pair(2, nil)) - >>> print(s) - (1 2) - >>> print(s.map(lambda x: x+4)) - (5 6) - """ - def __init__(self, first, rest): - self.first = first - self.rest = rest - - def __repr__(self): - return 'Pair({0}, {1})'.format(repr(self.first), repr(self.rest)) - - def __str__(self): - s = '(' + str(self.first) - rest = self.rest - while isinstance(rest, Pair): - s += ' ' + str(rest.first) - rest = rest.rest - if rest is not nil: - s += ' . ' + str(rest) - return s + ')' - - def __len__(self): - n, rest = 1, self.rest - while isinstance(rest, Pair): - n += 1 - rest = rest.rest - if rest is not nil: - raise TypeError('length attempted on improper list') - return n - - def __eq__(self, p): - if not isinstance(p, Pair): - return False - return self.first == p.first and self.rest == p.rest - - def map(self, fn): - """Return a Scheme list after mapping Python function FN to SELF.""" - mapped = fn(self.first) - if self.rest is nil or isinstance(self.rest, Pair): - return Pair(mapped, self.rest.map(fn)) - else: - raise TypeError('ill-formed list') - -class nil: - """The empty list""" - - def __repr__(self): - return 'nil' - - def __str__(self): - return '()' - - def __len__(self): - return 0 - - def map(self, fn): - return self - -nil = nil() # Assignment hides the nil class; there is only one instance - diff --git a/lab/sol-lab10/lab10.zip b/lab/sol-lab10/lab10.zip deleted file mode 100644 index 48a0dc6f5d..0000000000 Binary files a/lab/sol-lab10/lab10.zip and /dev/null differ diff --git a/lab/sol-lab10/ok b/lab/sol-lab10/ok deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/lab/sol-lab10/ok and /dev/null differ diff --git a/lab/sol-lab10/tests/using_pair.py b/lab/sol-lab10/tests/using_pair.py deleted file mode 100644 index 4124c28f56..0000000000 --- a/lab/sol-lab10/tests/using_pair.py +++ /dev/null @@ -1,100 +0,0 @@ -test = { - 'name': 'using-pair', - 'points': 0, - 'suites': [ - { - 'type': 'concept', - 'cases': [ - { - 'question': """ - Write out the Python expression that returns a `Pair` representing the given expression: (+ (- 2 4) 6 8) - """, - 'choices': [ - "Pair('+', Pair('-', Pair(2, Pair(4, Pair(6, Pair(8, nil))))))", - "Pair('+', Pair(Pair('-', Pair(2, Pair(4))), Pair(6, Pair(8))))", - "Pair(+, Pair(Pair(-, Pair(2, Pair(4, nil))), Pair(6, Pair(8, nil))))", - "Pair('+', Pair(Pair('-', Pair(2, Pair(4, nil))), Pair(6, Pair(8, nil))))", - "None of these" - ], - 'answer': "Pair('+', Pair(Pair('-', Pair(2, Pair(4, nil))), Pair(6, Pair(8, nil))))", - 'hidden': False - }, - { - 'question': """ - What is the operator of the previous part's call expression? - """, - 'choices': [ - '-', - '+', - '(', - '2', - '6', - 'None of these' - ], - 'answer': "+", - 'hidden': False - }, - { - 'question': """ - If the `Pair` you constructed in the previous part was bound to the name `p`, - how would you retrieve the operator? - """, - 'choices': [ - 'p', - 'p.first', - 'p.rest', - 'p.rest.first', - 'p.first.rest' - ], - 'answer': "p.first", - 'hidden': False - }, - { - 'question': """ - If the `Pair` you constructed was bound to the name `p`, - how would you retrieve a list containing all of the operands? - """, - 'choices': [ - 'p', - 'p.first', - 'p.rest', - 'p.rest.first', - 'p.first.rest' - ], - 'answer': "p.rest", - 'hidden': False - }, - { - 'question': """ - How would you retrieve only the first operand? - """, - 'choices': [ - 'p', - 'p.first', - 'p.rest', - 'p.rest.first', - 'p.first.rest' - ], - 'answer': "p.rest.first", - 'hidden': False - }, - { - 'question': """ - What is the first operand of the call expression (+ (- 2 4) 6 8) prior to evaluation? - """, - 'choices': [ - "'-'", - "'+'", - "2", - "4", - "-2", - "Pair('-', Pair(2, Pair(4, nil)))", - "Pair(2, Pair(4, nil))" - ], - 'answer': "Pair('-', Pair(2, Pair(4, nil)))", - 'hidden': False - } - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab11/index.html b/lab/sol-lab11/index.html deleted file mode 100644 index d31684be0e..0000000000 --- a/lab/sol-lab11/index.html +++ /dev/null @@ -1,623 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Lab 11 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Lab 11 Solutions - - - - - - -

-
- -

Solution Files

- - - - - - - - -

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.

- - -

YouTube link

-
- - -

Quasiquotation

- - -

Consult the drop-down if you need a refresher on quasiquotation. It's -okay to skip directly to the questions and refer back -here should you get stuck.

- - - -
- -

The normal quote ' and the quasiquote ` are both valid ways to quote an -expression. However, the quasiquoted expression can be unquoted with the -"unquote" , (represented by a comma). When a term in a quasiquoted expression -is unquoted, the unquoted term is evaluated, instead of being taken as literal text. -This mechanism is somewhat akin to using f-strings in Python, where expressions -inside {} are evaluated and inserted into the string.

- -
scm> (define a 5)
-a
-scm> (define b 3)
-b
-scm> `(* a b)  ; Quasiquoted expression
-(* a b)
-scm> `(* a ,b)  ; Unquoted b, which evaluates to 3
-(* a 3)
-scm> `(* ,(+ a b) b)  ; Unquoted (+ a b), which evaluates to 8
-(* 8 b)
- - - -
- - -

Q1: WWSD: Quasiquote

- - -

Use Ok to test your knowledge with the following "What Would Scheme Display?" -questions:

- -
python3 ok -q wwsd-quasiquote -u
- - - - - -
scm> '(1 x 3)
-
-scm> (define x 2)
-
-scm> `(1 x 3)
-
-scm> `(1 ,x 3)
-
-scm> `(1 x ,3)
-
-scm> `(1 (,x) 3)
-
-scm> `(1 ,(+ x 2) 3)
-
-scm> (define y 3)
-
-scm> `(x ,(* y x) y)
-
-scm> `(1 ,(cons x (list y 4)) 5)
- - -
- - - - -

Programs as Data

- - -

Consult the drop-down if you need a refresher on Programs as Data. It's -okay to skip directly to the questions and refer back -here should you get stuck.

- - - -
- -

All Scheme programs are made up of expressions. -There are two types of expressions: primitive (a.k.a atomic) expressions and combinations. -Here are some examples of each:

- -
    -
  • Primitive/atomic expression: #f, 1.7, +
  • -
  • Combinations: (factorial 10), (/ 8 3), (not #f)
  • -
- -

Scheme represents combinations as a Scheme list. Therefore, a combination can be constructed through list manipulation.

- -

For example, the expression (list '+ 2 2) evaluates to the list (+ 2 2), which is also an expression. If we then call eval on this list, it will evaluate to 4. The eval procedure takes in one argument expr and evaluates expr in the current environment.

- -
scm> (define expr (list '+ 2 2))
-expr
-scm> expr
-(+ 2 2)
-scm> (eval expr)
-4
- - - -

Additionally, quasiquotation is very helpful for building procedures that create expressions. Take a look at the following add-program:

- -
scm> (define (add-program x y)
-...>     `(+ ,x ,y))
-add-program
-scm> (add-program 3 6)
-(+ 3 6)
- - - -

add-program takes in two inputs x and y and returns an expression that, if evaluated, evaluates to the result of adding x and y together. -Within add-program, we use a quasiquote to build the addition expression (+ ...), and we unquote x and y to get their evaluated values in the -addition expression.

- -
- - -

Q2: If Program

- - -

In Scheme, the if special form allows us to evaluate one of two expressions based on a predicate. Write a program if-program that takes in the following parameters:

- -
    -
  1. predicate : a quoted expression which will evaluate to the condition in our if-expression
  2. -
  3. if-true : a quoted expression which will evaluate to the value we want to return if predicate evaluates to true (#t)
  4. -
  5. if-false : a quoted expression which will evaluate to the value we want to return if predicate evaluates to false (#f)
  6. -
- -

The program returns a Scheme list that represents an if expression in the form: (if <predicate> <if-true> <if-false>). Evaluating this -expression returns the result of evaluating this if expression.

- -

Here are some doctests to show this:

- -
scm> (define x 1)
-scm> (if-program '(= 0 0) '(+ x 1) 'x)
-(if (= 0 0) (+ x 1) x)
-scm> (eval (if-program '(= 0 0) '(+ x 1) 'x))
-2
-scm> (if-program '(= 1 0) '(print 3) '(print 5))
-(if (= 1 0) (print 3) (print 5))
-scm> (eval (if-program '(= 1 0) '(print 3) '(print 5)))
-5
- - - -
- - - -
(define (if-program condition if-true if-false)
-
`(if ,condition ,if-true ,if-false)
)
- -
- -

Use Ok to test your code:

python3 ok -q if-program
- -
- - -

Q3: Exponential Powers

- - -

Implement a procedure (pow-expr base exp) that returns an expression that, -when evaluated, raises the number base to the power of the nonnegative integer -exp. The body of pow-expr should not perform any multiplication (or -exponentiation). Instead, it should just construct an expression containing only -the symbols square and * as well as the number base and parentheses. The -length of this expression should grow logarithmically with respect to exp, -rather than linearly.

- -

Examples:

- -
scm> (pow-expr 2 0)
-1
-scm> (pow-expr 2 1)
-(* 2 1)
-scm> (pow-expr 2 5)
-(* 2 (square (square (* 2 1))))
-scm> (pow-expr 2 15)
-(* 2 (square (* 2 (square (* 2 (square (* 2 1)))))))
-scm> (pow-expr 2 16)
-(square (square (square (square (* 2 1)))))
-scm> (eval (pow-expr 2 16))
-65536
- - - -

Hint:

- -
    -
  1. x2y = (xy)2
  2. -
  3. x2y+1 = x(xy)2
  4. -
- -

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.

- -

Here's the solution to a similar homework problem.

- - - -
(define (square n) (* n n))
-
-(define (pow-expr base exp)
-
(cond ((= exp 0) 1) - ((even? exp) `(square ,(pow-expr base (/ exp 2)))) - (else `(* ,base ,(pow-expr base (- exp 1)))))
)
- -
- -

Use Ok to test your code:

python3 ok -q pow
- -
- - -

Macros

- - -

A macro is a code transformation that is created using define-macro and -applied using a call expression. A macro call is evaluated by:

- -
    -
  1. Binding the formal paramters of the macro to the unevaluated operand expressions of the macro call.
  2. -
  3. Evaluating the body of the macro, which returns an expression.
  4. -
  5. Evaluating the expression returned by the macro in the frame of the original macro call.
  6. -
- -
scm> (define-macro (twice expr) (list 'begin expr expr))
-twice
-scm> (twice (+ 2 2))  ; evaluates (begin (+ 2 2) (+ 2 2))
-4
-scm> (twice (print (+ 2 2)))  ; evaluates (begin (print (+ 2 2)) (print (+ 2 2)))
-4
-4
- - - - -

Q4: Repeat

- - -

Define repeat, a macro that is called on a number n and an expression -expr. Calling it evaluates expr in a local frame n times, and its value is -the final result. You will find the helper function repeated-call useful, which -takes a number n and a zero-argument procedure f and calls f n times.

- -

For example, (repeat (+ 2 3) (print 1)) is equivalent to:

- -

(repeated-call (+ 2 3) (lambda () (print 1)))

- -

and should evaluate (print 1) repeatedly 5 times.

- -

The following expression should print four four times:

- -

(repeat 2 (repeat 2 (print 'four)))

- - - -
(define-macro (repeat n expr)
-
`(repeated-call ,n (lambda () ,expr)))
-; Call zero-argument procedure f n times and return the final result. -(define (repeated-call n f) -
(if (= n 1) (f) (begin (f) (repeated-call (- n 1) f))))
- -
- -

Use Ok to test your code:

python3 ok -q repeat-lambda
- -
- - - -
- -

The repeated-call procedure takes a zero-argument procedure, so -(lambda () ___) must appear in the blank. The body of the lambda -is expr, which must be unquoted.

- -
- - - -
- -

Call f on no arguments with (f). If n is 1, just call f. If n is -greater than 1, first call f and then call (repeated-call (- n 1) f).

- -
- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/lab/sol-lab11/lab11.ok b/lab/sol-lab11/lab11.ok deleted file mode 100644 index 66ec94492d..0000000000 --- a/lab/sol-lab11/lab11.ok +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "Lab 11", - "endpoint": "cal/cs61a/fa24/lab11", - "src": [ - "lab11.scm" - ], - "tests": { - "lab*.py": "doctest", - "tests/*.py": "ok_test" - }, - "default_tests": [], - "protocols": [ - "restore", - "file_contents", - "unlock", - "grading", - "analytics", - "backup" - ] -} \ No newline at end of file diff --git a/lab/sol-lab11/lab11.scm b/lab/sol-lab11/lab11.scm deleted file mode 100644 index b905267596..0000000000 --- a/lab/sol-lab11/lab11.scm +++ /dev/null @@ -1,24 +0,0 @@ -(define (if-program condition if-true if-false) - `(if ,condition - ,if-true - ,if-false)) - -(define (square n) (* n n)) - -(define (pow-expr base exp) - (cond - ((= exp 0) - 1) - ((even? exp) - `(square ,(pow-expr base (/ exp 2)))) - (else - `(* ,base ,(pow-expr base (- exp 1)))))) - -(define-macro (repeat n expr) - `(repeated-call ,n (lambda () ,expr))) - -; Call zero-argument procedure f n times and return the final result. -(define (repeated-call n f) - (if (= n 1) - (f) - (begin (f) (repeated-call (- n 1) f)))) diff --git a/lab/sol-lab11/lab11.sql b/lab/sol-lab11/lab11.sql deleted file mode 100644 index 47d874a9d1..0000000000 --- a/lab/sol-lab11/lab11.sql +++ /dev/null @@ -1,43 +0,0 @@ -CREATE TABLE finals AS - SELECT "RSF" AS hall, "61A" as course UNION - SELECT "Wheeler" , "61A" UNION - SELECT "Pimentel" , "61A" UNION - SELECT "Li Ka Shing", "61A" UNION - SELECT "Stanley" , "61A" UNION - SELECT "RSF" , "61B" UNION - SELECT "Wheeler" , "61B" UNION - SELECT "Morgan" , "61B" UNION - SELECT "Wheeler" , "61C" UNION - SELECT "Pimentel" , "61C" UNION - SELECT "Soda 310" , "61C" UNION - SELECT "Soda 306" , "10" UNION - SELECT "RSF" , "70"; - -CREATE TABLE sizes AS - SELECT "RSF" AS room, 900 as seats UNION - SELECT "Wheeler" , 700 UNION - SELECT "Pimentel" , 500 UNION - SELECT "Li Ka Shing", 300 UNION - SELECT "Stanley" , 300 UNION - SELECT "Morgan" , 100 UNION - SELECT "Soda 306" , 80 UNION - SELECT "Soda 310" , 40 UNION - SELECT "Soda 320" , 30; - -CREATE TABLE sharing AS - SELECT a.course, COUNT(DISTINCT a.hall) AS shared - FROM finals AS a, finals AS b WHERE a.hall = b.hall AND a.course != b.course - GROUP BY a.course; - -CREATE TABLE pairs AS - SELECT a.room || " and " || b.room || " together have " || (a.seats + b.seats) || " seats" AS rooms - FROM sizes AS a, sizes AS b WHERE a.room < b.room AND a.seats + b.seats >= 1000 - ORDER BY a.seats + b.seats DESC; - -CREATE TABLE big AS - SELECT course FROM finals, sizes WHERE hall=room GROUP BY course HAVING SUM(seats) >= 1000; - -CREATE TABLE remaining AS - SELECT course, SUM(seats) - MAX(seats) AS remaining - FROM finals, sizes WHERE hall=room GROUP BY course; - diff --git a/lab/sol-lab11/lab11.zip b/lab/sol-lab11/lab11.zip deleted file mode 100644 index aaa4b7b8fa..0000000000 Binary files a/lab/sol-lab11/lab11.zip and /dev/null differ diff --git a/lab/sol-lab11/ok b/lab/sol-lab11/ok deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/lab/sol-lab11/ok and /dev/null differ diff --git a/lab/sol-lab11/scheme b/lab/sol-lab11/scheme deleted file mode 100644 index 2174145cff..0000000000 Binary files a/lab/sol-lab11/scheme and /dev/null differ diff --git a/lab/sol-lab11/sqlite_shell.py b/lab/sol-lab11/sqlite_shell.py deleted file mode 100644 index cf1d3686fc..0000000000 --- a/lab/sol-lab11/sqlite_shell.py +++ /dev/null @@ -1,681 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the MIT license - -# A simple SQLite shell that uses the built-in Python adapter. - -import codecs -import io -import os -import sys -import sqlite3 -import time -import warnings - -try: FileNotFoundError -except NameError: FileNotFoundError = OSError - -if str != bytes: buffer = bytes -if str != bytes: unicode = str - -try: import msvcrt -except ImportError: msvcrt = None - -CP_UTF8 = 65001 -pythonapi = None -if msvcrt: - import ctypes - (BOOL, DWORD, HANDLE, UINT) = (ctypes.c_long, ctypes.c_ulong, ctypes.c_void_p, ctypes.c_uint) - GetConsoleCP = ctypes.WINFUNCTYPE(UINT)(('GetConsoleCP', ctypes.windll.kernel32)) - SetConsoleCP = ctypes.WINFUNCTYPE(BOOL, UINT)(('SetConsoleCP', ctypes.windll.kernel32)) - GetConsoleOutputCP = ctypes.WINFUNCTYPE(UINT)(('GetConsoleOutputCP', ctypes.windll.kernel32)) - SetConsoleOutputCP = ctypes.WINFUNCTYPE(BOOL, UINT)(('SetConsoleOutputCP', ctypes.windll.kernel32)) - GetConsoleMode = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.POINTER(DWORD), use_last_error=True)(('GetConsoleMode', ctypes.windll.kernel32)) - GetNumberOfConsoleInputEvents = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.POINTER(DWORD), use_last_error=True)(('GetNumberOfConsoleInputEvents', ctypes.windll.kernel32)) - ReadConsoleW = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.c_void_p, DWORD, ctypes.POINTER(DWORD), ctypes.c_void_p, use_last_error=True)(('ReadConsoleW', ctypes.windll.kernel32)) - WriteConsoleW = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.c_void_p, DWORD, ctypes.POINTER(DWORD), ctypes.c_void_p, use_last_error=True)(('WriteConsoleW', ctypes.windll.kernel32)) - class Py_buffer(ctypes.Structure): _fields_ = [('buf', ctypes.c_void_p), ('obj', ctypes.py_object), ('len', ctypes.c_ssize_t), ('itemsize', ctypes.c_ssize_t), ('readonly', ctypes.c_int), ('ndim', ctypes.c_int), ('format', ctypes.c_char_p), ('shape', ctypes.POINTER(ctypes.c_ssize_t)), ('strides', ctypes.POINTER(ctypes.c_ssize_t)), ('suboffsets', ctypes.POINTER(ctypes.c_ssize_t))] + ([('smalltable', ctypes.c_ssize_t * 2)] if sys.version_info[0] <= 2 else []) + [('internal', ctypes.c_void_p)] - try: from ctypes import pythonapi - except ImportError: pass -if pythonapi: - def getbuffer(b, writable): - arr = Py_buffer() - pythonapi.PyObject_GetBuffer(ctypes.py_object(b), ctypes.byref(arr), ctypes.c_int(writable)) - try: buf = (ctypes.c_ubyte * arr.len).from_address(arr.buf) - finally: pythonapi.PyBuffer_Release(ctypes.byref(arr)) - return buf - -ENCODING = 'utf-8' - -if sys.version_info[0] < 3: - class NotASurrogateError(Exception): pass - def surrogateescape_handler(exc): - # Source: https://github.com/PythonCharmers/python-future/blob/aef57391c0cd58bf840dff5e2bc2c8c0f5b0a1b4/src/future/utils/surrogateescape.py - mystring = exc.object[exc.start:exc.end] - try: - if isinstance(exc, UnicodeDecodeError): - decoded = [] - for ch in mystring: - if isinstance(ch, int): - code = ch - else: - code = ord(ch) - if 0x80 <= code <= 0xFF: - decoded.append(unichr(0xDC00 + code)) - elif code <= 0x7F: - decoded.append(unichr(code)) - else: - raise NotASurrogateError() - decoded = str().join(decoded) - elif isinstance(exc, UnicodeEncodeError): - decoded = [] - for ch in mystring: - code = ord(ch) - if not 0xD800 <= code <= 0xDCFF: - raise NotASurrogateError() - if 0xDC00 <= code <= 0xDC7F: - decoded.append(unichr(code - 0xDC00)) - elif code <= 0xDCFF: - decoded.append(unichr(code - 0xDC00)) - else: - raise NotASurrogateError() - decoded = str().join(decoded) - else: - raise exc - except NotASurrogateError: - raise exc - return (decoded, exc.end) - codecs.register_error('surrogateescape', surrogateescape_handler) - -def exception_encode(ex, codec): - if str == bytes: - reduced = ex.__reduce__() - ex = reduced[0](*tuple(map(lambda arg: codec.decode(arg)[0] if isinstance(arg, bytes) else arg, reduced[1]))) - return ex - -def sql_commands(read_line): - delims = ['"', "'", ';', '--'] - counter = 0 - in_string = None - j = i = 0 - prev_line = None - line = None - concat = [] - while True: - if line is None: - while True: # process preprocessor directives - counter += 1 - not_in_the_middle_of_any_input = not in_string and i == j and all(map(lambda chunk_: len(chunk_) == 0, concat)) - line = read_line(counter - 1, not_in_the_middle_of_any_input, prev_line) - empty_string = line[:0] if line is not None else line - prev_line = line - if not line: - break - if not_in_the_middle_of_any_input and line.startswith("."): - yield line - line = None - else: - break - if not line: - break - j = i = 0 - if j < len(line): - (j, delim) = min(map(lambda pair: pair if pair[0] >= 0 else (len(line), pair[1]), map(lambda d: (line.find(d, j), d), in_string or delims if in_string != '--' else "\n"))) - if i < j: concat.append(line[i:j]); i = j - if not in_string: - if j < len(line): - j += len(delim) - if delim == ';': - i = j - concat.append(line[j : j + len(delim)]) # ensure delimeter is the same type as the string (it may not be due to implicit conversion) - # Eat up any further spaces until a newline - while j < len(line): - delim = line[j:j+1] - if not delim.isspace(): break - j += 1 - if delim == "\n": break - if i < j: concat.append(line[i:j]); i = j - yield empty_string.join(concat) - del concat[:] - else: - in_string = delim - else: - if j < len(line): - ch = line[j:j+1] - assert ch == in_string or in_string == '--' - j += 1 - i = j - concat.append(ch) - in_string = None - else: - if i < j: concat.append(line[i:j]); i = j - line = None - -class WindowsConsoleIOMixin(object): - # Ctrl+C handling with ReadFile() is messed up on Windows starting on Windows 8... here's some background reading: - # https://stackoverflow.com/a/43260436 - # https://github.com/microsoft/terminal/issues/334 - # We use ReadConsole when we can, so it doesn't affect us, but it's good info to know regardless. - def __init__(self, fd): - assert isatty(fd), "file descriptor must refer to a console (note that on Windows, NUL satisfies isatty(), but is not a console)" - self.fd = fd - self.handle = msvcrt.get_osfhandle(fd) - def fileno(self): return self.fd - def isatty(self): return isatty(self.fd) - def seekable(self): return False - def readable(self): return GetNumberOfConsoleInputEvents(self.handle, ctypes.byref(DWORD(0))) != 0 - def writable(self): n = DWORD(0); return WriteConsoleW(self.handle, ctypes.c_void_p(), n, ctypes.byref(n), ctypes.c_void_p()) != 0 - def readwcharsinto(self, buf, n): - nr = DWORD(n) - old_error = ctypes.get_last_error() - ctypes.set_last_error(0) - success = ReadConsoleW(self.handle, buf, nr, ctypes.byref(nr), ctypes.c_void_p()) - error = ctypes.get_last_error() - ctypes.set_last_error(old_error) - if not success: raise ctypes.WinError(error) - ERROR_OPERATION_ABORTED = 995 - if nr.value == 0 and error == ERROR_OPERATION_ABORTED: - # Apparently this can trigger pending KeyboardInterrupts? - time.sleep(1.0 / (1 << 64)) - raise KeyboardInterrupt() # If Python doesn't raise it, we can - return nr.value - def writewchars(self, buf, n): - nw = DWORD(n) - if not WriteConsoleW(self.handle, buf, nw, ctypes.byref(nw), ctypes.c_void_p()): - raise ctypes.WinError() - return nw.value - -class WindowsConsoleRawIO(WindowsConsoleIOMixin, io.RawIOBase): - def readinto(self, b): - wordsize = ctypes.sizeof(ctypes.c_wchar) - return self.readwcharsinto(getbuffer(b, True), len(b) // wordsize) * wordsize - def write(self, b): - wordsize = ctypes.sizeof(ctypes.c_wchar) - return self.writewchars(getbuffer(b, False), len(b) // wordsize) * wordsize - -class WindowsConsoleTextIO(WindowsConsoleIOMixin, io.TextIOBase): - buf = None - buffered = unicode() - translate = True - def getbuf(self, ncodeunits): - buf = self.buf - if buf is None or len(buf) < ncodeunits: - self.buf = buf = ctypes.create_unicode_buffer(ncodeunits) - return buf - @staticmethod # Don't let classes override this... they can override the caller instead - def do_read(self, nchars, translate_newlines): - prenewline = os.linesep[:-1] - newline = os.linesep[-1:] - empty = os.linesep[:0] - if nchars is None or nchars < -1: nchars = -1 - ncodeunits = nchars if nchars >= 0 else io.DEFAULT_BUFFER_SIZE # Unit mismatch, but doesn't matter; we'll loop - buf = None - istart = 0 - while True: - iend = self.buffered.find(newline, istart, min(istart + nchars, len(self.buffered)) if nchars >= 0 else None) if newline is not None else nchars - if iend >= 0: iend += len(newline) if newline is not None else 0 - if 0 <= iend <= len(self.buffered): - break - if buf is None: buf = self.getbuf(ncodeunits) - istart = len(self.buffered) - chunk = buf[:self.readwcharsinto(buf, ncodeunits)] - if translate_newlines: chunk = chunk.replace(prenewline, empty) - if chunk.startswith('\x1A'): # EOF on Windows (Ctrl+Z) at the beginning of a line results in the entire rest of the buffer being discarded - iend = istart - break - # Python 2 and Python 3 behaviors differ on Windows... Python 2's sys.stdin.readline() just deletes the next character if it sees EOF in the middle of a string! I won't emulate that here. - self.buffered += chunk # We're relying on Python's concatenation optimization here... we don't do it ourselves, since we want self.buffered to be valid every iteration in case there is an exception raised - result = self.buffered[:iend] - self.buffered = self.buffered[iend:] - return result - def read(self, nchars=-1): return WindowsConsoleTextIO.do_read(self, nchars, None, self.translate) - def readline(self, nchars=-1): return WindowsConsoleTextIO.do_read(self, nchars, self.translate) - def write(self, text): buf = ctypes.create_unicode_buffer(text); return self.writewchars(buf, max(len(buf) - 1, 0)) - -def wrap_windows_console_io(stream, is_output): - fd = None - if stream is not None and sys.version_info[0] < 3 and msvcrt and (is_output or pythonapi) and isatty(stream): - try: fd = stream.fileno() - except io.UnsupportedOperation: pass - result = stream - if fd is not None: - f = GetConsoleOutputCP if is_output else GetConsoleCP - if not f or f() != CP_UTF8: - try: - if True or is_output: - result = WindowsConsoleTextIO(fd) - else: - result = io.TextIOWrapper((io.BufferedWriter if is_output else io.BufferedReader)(WindowsConsoleRawIO(fd)), 'utf-16-le', 'strict', line_buffering=True) - except IOError: pass - return result - -class NonOwningTextIOWrapper(io.TextIOWrapper): - def __init__(self, base_textiowrapper, **kwargs): - assert isinstance(base_textiowrapper, io.TextIOWrapper) - self.base = base_textiowrapper # must keep a reference to this alive so it doesn't get closed - super(NonOwningTextIOWrapper, self).__init__(base_textiowrapper.buffer, **kwargs) - def close(self): - super(NonOwningTextIOWrapper, self).flush() - -def wrap_unicode_stdio(stream, is_writer, encoding): # The underlying stream must NOT be used directly until the stream returned by this function is disposed of - if isinstance(stream, io.TextIOWrapper): - stream.flush() # Make sure nothing is left in the buffer before we re-wrap it - none = object() - kwargs = {} - for key in ['encoding', 'errors', 'newline', 'line_buffering', 'write_through']: - value = getattr(stream, 'newlines' if key == 'newline' else key, none) - if value is not none: - kwargs[key] = value - kwargs['encoding'] = encoding - result = NonOwningTextIOWrapper(stream, **kwargs) - elif 'PYTHONIOENCODING' not in os.environ and str == bytes and stream in (sys.stdin, sys.stdout, sys.stderr): - result = (codecs.getwriter if is_writer else codecs.getreader)(encoding)(stream) - else: - result = stream - return result - -class StringEscapeParser(object): - def __init__(self): - import re - self.pattern = re.compile("\"((?:[^\"\\n]+|\\\\.)*)(?:\"|$)|\'([^\'\\n]*)(?:\'|$)|(\\S+)") - self.escape_pattern = re.compile("\\\\(.)", re.DOTALL) - @staticmethod - def escape_replacement(m): - text = m.group(1) - if text == "\\": text = "\\" - elif text == "/": text = "\n" - elif text == "n": text = "\n" - elif text == "r": text = "\r" - elif text == "t": text = "\t" - elif text == "v": text = "\v" - elif text == "f": text = "\f" - elif text == "a": text = "\a" - elif text == "b": text = "\b" - return text - def __call__(self, s): - escape_pattern = self.escape_pattern - escape_replacement = self.escape_replacement - result = [] - for match in self.pattern.finditer(s): - [m1, m2, m3] = match.groups() - if m1 is not None: result.append(escape_pattern.sub(escape_replacement, m1)) - if m2 is not None: result.append(m2) - if m3 is not None: result.append(escape_pattern.sub(escape_replacement, m3)) - return result - -class Database(object): - def __init__(self, name, *args, **kwargs): - self.connection = sqlite3.connect(name, *args, **kwargs) - self.cursor = self.connection.cursor() - self.name = name # assign name only AFTER cursor is created - -def isatty(file_or_fd): - result = True - method = getattr(file_or_fd, 'isatty', None) if not isinstance(file_or_fd, int) else None # this check is just an optimization - if method is not None: - try: tty = method() - except io.UnsupportedOperation: tty = None - result = result and tty is not None and tty - method = getattr(file_or_fd, 'fileno', None) if not isinstance(file_or_fd, int) else None # this check is just an optimization - if method is not None: - try: fd = method() - except io.UnsupportedOperation: fd = None - result = result and fd is not None and os.isatty(fd) and (not msvcrt or GetConsoleMode(msvcrt.get_osfhandle(fd), ctypes.byref(DWORD(0))) != 0) - return result - -def can_call_input_for_stdio(stream): - return stream == sys.stdin and sys.version_info[0] >= 3 - -class StdIOProxy(object): - # Maybe useful later: codecs.StreamRecoder(bytesIO, codec.decode, codec.encode, codec.streamwriter, codec.streamreader, errors='surrogateescape') - def __init__(self, stdin, stdout, stderr, codec, allow_set_code_page): - self.codec = codec - streams = (stdin, stdout, stderr) - for stream in streams: - assert isinstance(stream, io.IOBase) or sys.version_info[0] < 3 and isinstance(stream, file) or hasattr(stream, 'mode'), "unable to determine stream type" - assert not isinstance(stream, io.RawIOBase), "RAW I/O APIs are different and not supported" - self.streaminfos = tuple(map(lambda stream: - ( - stream, - isinstance(stream, io.BufferedIOBase) or isinstance(stream, io.RawIOBase) or not isinstance(stream, io.TextIOBase) and 'b' in stream.mode, - isinstance(stream, io.TextIOBase) or not (isinstance(stream, io.BufferedIOBase) or isinstance(stream, io.RawIOBase)) and 'b' not in stream.mode, - allow_set_code_page - ), - streams)) - @property - def stdin(self): return self.streaminfos[0][0] - @property - def stdout(self): return self.streaminfos[1][0] - @property - def stderr(self): return self.streaminfos[2][0] - def _coerce(self, streaminfo, codec, arg): - stream = streaminfo[0] - can_binary = streaminfo[1] - can_text = streaminfo[2] - if not isinstance(arg, bytes) and not isinstance(arg, buffer) and not isinstance(arg, unicode): - arg = unicode(arg) - if isinstance(arg, bytes) or isinstance(arg, buffer): - if not can_binary: - arg = codec.decode(arg, 'surrogateescape')[0] - elif isinstance(arg, unicode): - if not can_text: - arg = codec.encode(unicode(arg), 'strict')[0] - return arg - @staticmethod - def _do_readline(stream, allow_set_code_page, *args): - new_code_page = CP_UTF8 - old_code_page = GetConsoleCP() if msvcrt and GetConsoleCP and isatty(stream) else None - if old_code_page == new_code_page: old_code_page = None # Don't change code page if it's already correct... - if old_code_page is not None: - if not SetConsoleCP(new_code_page): - old_code_page = None - try: - result = stream.readline(*args) - finally: - if old_code_page is not None: - SetConsoleCP(old_code_page) - return result - @staticmethod - def _do_write(stream, allow_set_code_page, *args): - new_code_page = CP_UTF8 - old_code_page = GetConsoleOutputCP() if msvcrt and GetConsoleOutputCP and isatty(stream) else None - if old_code_page == new_code_page: old_code_page = None # Don't change code page if it's already correct... - if old_code_page is not None: - if not SetConsoleOutputCP(new_code_page): - old_code_page = None - try: - result = stream.write(*args) - finally: - if old_code_page is not None: - SetConsoleCP(old_code_page) - return result - def _readln(self, streaminfo, codec, prompt): - stream = streaminfo[0] - can_binary = streaminfo[1] - allow_set_code_page = streaminfo[3] - if can_call_input_for_stdio(stream) and not can_binary: # input() can't work with binary data - result = self._coerce(streaminfo, codec, "") - try: - result = input(*((self._coerce(streaminfo, codec, prompt),) if prompt is not None else ())) - result += self._coerce(streaminfo, codec, "\n") - except EOFError: pass - else: - self.output(*((prompt,) if prompt is not None else ())) - self.error() - result = StdIOProxy._do_readline(stream, allow_set_code_page) - return result - def _writeln(self, streaminfo, codec, *args, **kwargs): - stream = streaminfo[0] - allow_set_code_page = streaminfo[3] - flush = kwargs.pop('flush', True) - kwargs.setdefault('end', '\n') - kwargs.setdefault('sep', ' ') - end = kwargs.get('end') - sep = kwargs.get('sep') - first = True - for arg in args: - if first: first = False - elif sep is not None: - StdIOProxy._do_write(stream, allow_set_code_page, self._coerce(streaminfo, codec, sep)) - StdIOProxy._do_write(stream, allow_set_code_page, self._coerce(streaminfo, codec, arg)) - if end is not None: - StdIOProxy._do_write(stream, allow_set_code_page, self._coerce(streaminfo, codec, end)) - if flush: stream.flush() - def inputln(self, prompt=None): return self._readln(self.streaminfos[0], self.codec, prompt) - def output(self, *args, **kwargs): kwargs.setdefault('end', None); return self._writeln(self.streaminfos[1], self.codec, *args, **kwargs) - def outputln(self, *args, **kwargs): return self._writeln(self.streaminfos[1], self.codec, *args, **kwargs) - def error(self, *args, **kwargs): kwargs.setdefault('end', None); return self._writeln(self.streaminfos[2], self.codec, *args, **kwargs) - def errorln(self, *args, **kwargs): return self._writeln(self.streaminfos[2], self.codec, *args, **kwargs) - -class bytes_comparable_with_unicode(bytes): # For Python 2/3 compatibility, to allow implicit conversion between strings and bytes when it is safe. (Used for strings like literals which we know be safe.) - codec = codecs.lookup('ascii') # MUST be a safe encoding - @classmethod - def coerce(cls, other, for_output=False): - return cls.codec.encode(other)[0] if not isinstance(other, bytes) else bytes_comparable_with_unicode(other) if for_output else other - @classmethod - def translate_if_bytes(cls, value): - if value is not None and isinstance(value, bytes): value = cls(value) - return value - def __hash__(self): return super(bytes_comparable_with_unicode, self).__hash__() # To avoid warning - def __eq__(self, other): return super(bytes_comparable_with_unicode, self).__eq__(self.coerce(other)) - def __ne__(self, other): return super(bytes_comparable_with_unicode, self).__ne__(self.coerce(other)) - def __lt__(self, other): return super(bytes_comparable_with_unicode, self).__lt__(self.coerce(other)) - def __gt__(self, other): return super(bytes_comparable_with_unicode, self).__gt__(self.coerce(other)) - def __le__(self, other): return super(bytes_comparable_with_unicode, self).__le__(self.coerce(other)) - def __ge__(self, other): return super(bytes_comparable_with_unicode, self).__ge__(self.coerce(other)) - def __getitem__(self, index): return self.coerce(super(bytes_comparable_with_unicode, self).__getitem__(index), True) - def __add__(self, other): return self.coerce(super(bytes_comparable_with_unicode, self).__add__(self.coerce(other)), True) - def __iadd__(self, other): return self.coerce(super(bytes_comparable_with_unicode, self).__iadd__(self.coerce(other)), True) - def __radd__(self, other): return self.coerce(self.coerce(other).__add__(self), True) - def find(self, other, *args): return super(bytes_comparable_with_unicode, self).find(self.coerce(other), *args) - def join(self, others): return self.coerce(super(bytes_comparable_with_unicode, self).join(map(self.coerce, others)), True) - def startswith(self, other): return super(bytes_comparable_with_unicode, self).startswith(self.coerce(other)) - def __str__(self): return self.codec.decode(self)[0] - if str == bytes: - __unicode__ = __str__ - def __str__(self): raise NotImplementedError() - -def wrap_bytes_comparable_with_unicode_readline(readline): - def callback(*args): - line = readline(*args) - line = bytes_comparable_with_unicode.translate_if_bytes(line) - return line - return callback - -def main(program, *args, **kwargs): # **kwargs = dict(stdin=file, stdout=file, stderr=file); useful for callers who import this module - import argparse # slow import (compiles regexes etc.), so don't import it until needed - argparser = argparse.ArgumentParser( - prog=os.path.basename(program), - usage=None, - description=None, - epilog=None, - parents=[], - formatter_class=argparse.RawTextHelpFormatter) - argparser.add_argument('-version', '--version', action='store_true', help="show SQLite version") - argparser.add_argument('-batch', '--batch', action='store_true', help="force batch I/O") - argparser.add_argument('-init', '--init', metavar="FILE", help="read/process named file") - argparser.add_argument('filename', nargs='?', metavar="FILENAME", help="is the name of an SQLite database.\nA new database is created if the file does not previously exist.") - argparser.add_argument('sql', nargs='*', metavar="SQL", help="SQL commnds to execute after opening database") - argparser.add_argument('--readline', action='store', metavar="(true|false)", default="true", choices=("true", "false"), help="whether to import readline if available (default: %(default)s)") - argparser.add_argument('--self-test', action='store_true', help="perform a basic self-test") - argparser.add_argument('--cross-test', action='store_true', help="perform a basic test against the official executable") - argparser.add_argument('--unicode-stdio', action='store', metavar="(true|false)", default="true", choices=("true", "false"), help="whether to enable Unicode wrapper for standard I/O (default: %(default)s)") - argparser.add_argument('--console', action='store', metavar="(true|false)", default="true", choices=("true", "false"), help="whether to auto-detect and use console window APIs (default: %(default)s)") - argparser.add_argument('--encoding', default=ENCODING, help="the default encoding to use (default: %(default)s)") - (stdin, stdout, stderr) = (kwargs.pop('stdin', sys.stdin), kwargs.pop('stdout', sys.stdout), kwargs.pop('stderr', sys.stderr)) - parsed_args = argparser.parse_args(args) - codec = codecs.lookup(parsed_args.encoding or argparser.get_default('encoding')) - if parsed_args.self_test: self_test(codec) - if parsed_args.cross_test: cross_test("sqlite3", codec) - parse_escaped_strings = StringEscapeParser() - if parsed_args.unicode_stdio == "true": - stdin = wrap_unicode_stdio(stdin, False, codec.name) - stdout = wrap_unicode_stdio(stdout, True, codec.name) - stderr = wrap_unicode_stdio(stderr, True, codec.name) - if parsed_args.console == "true": - stdin = wrap_windows_console_io(stdin, False) - stdout = wrap_windows_console_io(stdout, True) - stderr = wrap_windows_console_io(stderr, True) - allow_set_code_page = sys.version_info[0] < 3 and False # This is only necessary on Python 2 if we use the default I/O functions instead of bypassing to ReadConsole()/WriteConsole() - stdio = StdIOProxy(stdin, stdout, stderr, codec, allow_set_code_page) - db = None - no_args = len(args) == 0 - init_sql = parsed_args.sql - is_nonpipe_input = stdin.isatty() # NOT the same thing as TTY! (NUL and /dev/null are the difference) - init_show_prompt = not parsed_args.batch and is_nonpipe_input - if not parsed_args.batch and isatty(stdin) and (parsed_args.readline == "true" or __name__ == '__main__') and parsed_args.readline != "false": - try: - with warnings.catch_warnings(): - warnings.filterwarnings('ignore', category=DeprecationWarning) - import readline - except ImportError: pass - if parsed_args and parsed_args.version: - stdio.outputln(sqlite3.sqlite_version); - else: - filename = parsed_args.filename - if filename is None: filename = ":memory:" - db = Database(filename, isolation_level=None) - def exec_script(db, filename, ignore_io_errors): - try: - with io.open(filename, 'r', encoding=codec.name) as f: # Assume .sql files are text -- any binary data inside them should be X'' encoded, not embedded directly - for command in sql_commands(wrap_bytes_comparable_with_unicode_readline(lambda *args: (lambda s: (s) or None)(f.readline()))): - result = exec_command(db, command, False and ignore_io_errors) - if result is not None: - return result - except IOError as ex: - stdio.errorln(ex) - if not ignore_io_errors: return ex.errno - def raise_invalid_command_error(command): - if isinstance(command, bytes): command = codec.decode(command)[0] - if command.startswith("."): command = command[1:] - raise RuntimeError("Error: unknown command or invalid arguments: \"%s\". Enter \".help\" for help" % (command.rstrip().replace("\\", "\\\\").replace("\"", "\\\""),)) - def exec_command(db, command, ignore_io_errors): - results = None - query = None - query_parameters = {} - try: - if command.startswith("."): - args = list(parse_escaped_strings(command)) - if args[0] in (".quit", ".exit"): - return 0 - elif args[0] == ".help": - stdio.error(""" -.cd DIRECTORY Change the working directory to DIRECTORY -.dump Dump the database in an SQL text format -.exit Exit this program -.help Show this message -.open FILE Close existing database and reopen FILE -.print STRING... Print literal STRING -.quit Exit this program -.read FILENAME Execute SQL in FILENAME -.schema ?PATTERN? Show the CREATE statements matching PATTERN -.show Show the current values for various settings -.tables ?TABLE? List names of tables -""".lstrip()) - elif args[0] == ".cd": - if len(args) != 2: raise_invalid_command_error(command) - os.chdir(args[1]) - elif args[0] == ".dump": - if len(args) != 1: raise_invalid_command_error(command) - foreign_keys = db.cursor.execute("PRAGMA foreign_keys;").fetchone()[0] - if foreign_keys in (0, "0", "off", "OFF"): - stdio.outputln("PRAGMA foreign_keys=OFF;", flush=False) - for line in db.connection.iterdump(): - stdio.outputln(line, flush=False) - stdio.output() - elif args[0] == ".open": - if len(args) <= 1: raise_invalid_command_error(command) - filename = args[-1] - for option in args[+1:-1]: - raise ValueError("option %s not supported" % (repr(option),)) - try: db.__init__(filename) - except sqlite3.OperationalError as ex: - ex.args = ex.args[:0] + ("Error: unable to open database \"%s\": %s" % (filename, ex.args[0]),) + ex.args[1:] - raise - elif args[0] == ".print": - stdio.outputln(*args[1:]) - elif args[0] == ".read": - if len(args) != 2: raise_invalid_command_error(command) - exec_script(db, args[1], ignore_io_errors) - elif args[0] == ".schema": - if len(args) > 2: raise_invalid_command_error(command) - pattern = args[1] if len(args) > 1 else None - query_parameters['type'] = 'table' - if pattern is not None: - query_parameters['pattern'] = pattern - query = "SELECT sql || ';' FROM sqlite_master WHERE type = :type" + (" AND name LIKE :pattern" if pattern is not None else "") + ";" - elif args[0] == ".show": - if len(args) > 2: raise_invalid_command_error(command) - stdio.errorln(" filename:", db.name) - elif args[0] == ".tables": - if len(args) > 2: raise_invalid_command_error(command) - pattern = args[1] if len(args) > 1 else None - query_parameters['type'] = 'table' - if pattern is not None: - query_parameters['pattern'] = pattern - query = "SELECT name FROM sqlite_master WHERE type = :type" + (" AND name LIKE :pattern" if pattern is not None else "") + ";" - else: - raise_invalid_command_error(args[0]) - else: - query = command - if query is not None: - results = db.cursor.execute(query if isinstance(query, unicode) else codec.decode(query, 'surrogatereplace')[0], query_parameters) - except (RuntimeError, OSError, FileNotFoundError, sqlite3.OperationalError) as ex: - stdio.errorln(exception_encode(ex, codec)) - if results is not None: - for row in results: - stdio.outputln(*tuple(map(lambda item: item if item is not None else "", row)), sep="|", flush=False) - stdio.output() - if db: - if parsed_args and parsed_args.init: - if is_nonpipe_input: stdio.errorln("-- Loading resources from", parsed_args.init) - exec_script(db, parsed_args.init, False) - def read_stdin(index, not_in_the_middle_of_any_input, prev_line): - show_prompt = init_show_prompt - to_write = [] - if index < len(init_sql): - line = init_sql[index] - if not line.startswith(".") and not line.rstrip().endswith(";"): - line += ";" - elif index == len(init_sql) and len(init_sql) > 0: - line = None - else: - if show_prompt: - if not_in_the_middle_of_any_input: - show_prompt = False - if index == 0: - to_write.append("SQLite version %s (adapter version %s)\nEnter \".help\" for usage hints.\n" % (sqlite3.sqlite_version, sqlite3.version)) - if no_args: - to_write.append("Connected to a transient in-memory database.\nUse \".open FILENAME\" to reopen on a persistent database.\n") - if index > 0 and not prev_line: - to_write.append("\n") - to_write.append("%7s " % ("sqlite%s>" % ("",) if not_in_the_middle_of_any_input else "...>",)) - try: - line = stdio.inputln("".join(to_write)) - except KeyboardInterrupt: - line = "" - raise # just kidding, don't handle it for now... - return line - for command in sql_commands(wrap_bytes_comparable_with_unicode_readline(read_stdin)): - result = exec_command(db, command, True) - if result is not None: - return result - if init_show_prompt and len(init_sql) == 0: - stdio.outputln() - -def call_program(cmdline, input_text): - import subprocess - return subprocess.Popen(cmdline, bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=False).communicate(input_text) - -def test_query(): - hexcodec = codecs.lookup('hex_codec') - ascii = 'ascii' - data1 = b"\xD8\xA2" - data2 = b"\x01\x02\xFF\x01\xFF\xFE\xFD" - values = [data1, data2] - query_bytes = b'SELECT %s;' % (b", ".join(map(lambda b: b"X'%s'" % (hexcodec.encode(b)[0].upper(),), values)),) - expected_bytes = b"%s\n" % (b"|".join(values),) - return query_bytes, expected_bytes - -def cross_test(sqlite_cmdline, codec): - (query_bytes, expected_bytes) = test_query() - (official_output, official_error) = call_program(sqlite_cmdline, query_bytes) - # We can't use os.linesep here since binaries may belong to different platforms (Win32/MinGW vs. MSYS/Cygwin vs. WSL...) - official_output = official_output.replace(b"\r\n", b"\n") - official_error = official_error.replace(b"\r\n", b"\n") - if official_output != expected_bytes: - raise sqlite3.ProgrammingError("expected bytes are wrong: official %s != expected %s" % (repr(official_output), repr(expected_bytes))) - if official_error: - raise sqlite3.ProgrammingError("did not expect errors from official binary") - -def self_test(codec): - (query_bytes, expected_bytes) = test_query() - if not (lambda stdin, stdout, stderr: not main(sys.argv[0], stdin=stdin, stdout=stdout, stderr=stderr) and stdout.getvalue() == expected_bytes)(io.BytesIO(query_bytes), io.BytesIO(), io.BytesIO()): - raise sqlite3.ProgrammingError("byte I/O is broken") - if not (lambda stdin, stdout, stderr: not main(sys.argv[0], stdin=stdin, stdout=stdout, stderr=stderr) and stdout.getvalue() == codec.decode(expected_bytes, 'surrogateescape'))(io.StringIO(query_bytes.decode(ascii)), io.StringIO(), io.StringIO()): - raise sqlite3.ProgrammingError("string I/O is broken") - -if __name__ == '__main__': - import sys - exit_code = main(*sys.argv) - if exit_code not in (None, 0): raise SystemExit(exit_code) diff --git a/lab/sol-lab11/tests/big.py b/lab/sol-lab11/tests/big.py deleted file mode 100644 index 5e9966dbad..0000000000 --- a/lab/sol-lab11/tests/big.py +++ /dev/null @@ -1,23 +0,0 @@ -test = { - 'name': 'big', - 'points': 1, - 'suites': [ - { - 'type': 'sqlite', - 'setup': """ - sqlite> .read lab11.sql - """, - 'cases': [ - { - 'locked': False, - 'code': r""" - sqlite> SELECT * FROM big; - 61A - 61B - 61C - """, - }, - ], - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab11/tests/if-program.py b/lab/sol-lab11/tests/if-program.py deleted file mode 100644 index 45073f903e..0000000000 --- a/lab/sol-lab11/tests/if-program.py +++ /dev/null @@ -1,34 +0,0 @@ -test = { - 'name': 'if-program', - 'points': 1, - 'suites': [ - { - 'type': 'scheme', - 'setup': r""" - scm> (load-all ".") - """, - 'cases': [ - { - 'code': r""" - scm> (define expr (if-program '(= 0 0) '2 '3)) - expr - scm> (eval expr) - 2 - """, - 'hidden': False, - 'locked' : False - }, - { - 'code': r""" - scm> (define expr2 (if-program '(= 1 0) '(print 3) '(print 5))) - expr2 - scm> (eval expr2) - 5 - """, - 'hidden': False, - 'locked' : False - } - ], - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab11/tests/kwlist_add.py b/lab/sol-lab11/tests/kwlist_add.py deleted file mode 100644 index ca5f53a2cf..0000000000 --- a/lab/sol-lab11/tests/kwlist_add.py +++ /dev/null @@ -1,76 +0,0 @@ -test = { - 'name': 'kwlist-add', - 'points': 1, - 'suites': [ - { - 'type': 'scheme', - 'setup': r""" - scm> (load-all ".") ; abstraction 1 - scm> (define make-kwlist make-kwlist1) - scm> (define get-keys-kwlist get-keys-kwlist1) - scm> (define get-values-kwlist get-values-kwlist1) - """, - 'cases': [ - { - 'code': r""" - scm> (define ex-lst (make-kwlist '(a b c) '(1 2 3))) - ex-lst - scm> (get-keys-kwlist ex-lst) - (a b c) - scm> (get-values-kwlist ex-lst) - (1 2 3) - scm> (define ex-lst (add-to-kwlist ex-lst 'd '4)) - ex-lst - scm> (get-keys-kwlist ex-lst) ; note that new items are at the end of the list! - (a b c d) - scm> (get-values-kwlist ex-lst) ; here too! - (1 2 3 4) - scm> (define ex-lst (add-to-kwlist ex-lst 'e '1)) - ex-lst - scm> (get-keys-kwlist ex-lst) ; duplicate keys are okay! - (a b c d e) - scm> (get-values-kwlist ex-lst) - (1 2 3 4 1) - """, - "locked": False, - "hidden": False - }, - ], - }, - { - 'type': 'scheme', - 'setup': r""" - scm> (load-all ".") ; abstraction 2 - scm> (define make-kwlist make-kwlist2) - scm> (define get-keys-kwlist get-keys-kwlist2) - scm> (define get-values-kwlist get-values-kwlist2) - """, - 'cases': [ - { - 'code': r""" - scm> (define ex-lst (make-kwlist '(a b c) '(1 2 3))) - ex-lst - scm> (get-keys-kwlist ex-lst) - (a b c) - scm> (get-values-kwlist ex-lst) - (1 2 3) - scm> (define ex-lst (add-to-kwlist ex-lst 'd '4)) - ex-lst - scm> (get-keys-kwlist ex-lst) ; note that new items are at the end of the list! - (a b c d) - scm> (get-values-kwlist ex-lst) ; here too! - (1 2 3 4) - scm> (define ex-lst (add-to-kwlist ex-lst 'e '1)) - ex-lst - scm> (get-keys-kwlist ex-lst) ; duplicate keys are okay! - (a b c d e) - scm> (get-values-kwlist ex-lst) - (1 2 3 4 1) - """, - "locked": False, - "hidden": False - }, - ], - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab11/tests/kwlist_construct.py b/lab/sol-lab11/tests/kwlist_construct.py deleted file mode 100644 index c6b0d6b541..0000000000 --- a/lab/sol-lab11/tests/kwlist_construct.py +++ /dev/null @@ -1,58 +0,0 @@ -test = { - 'name': 'kwlist-construct', - 'points': 1, - 'suites': [ - { - 'type': 'scheme', - 'setup': r""" - scm> (load-all ".") - """, - 'cases': [ - { - 'code': r""" - scm> (define ex-lst1 (make-kwlist1 '(a b c) '(1 2 3))) - ex-lst1 - scm> ex-lst1 - ((a b c) (1 2 3)) - scm> (get-keys-kwlist1 ex-lst1) - (a b c) - scm> (get-values-kwlist1 ex-lst1) - (1 2 3) - scm> (define ex-lst2 (make-kwlist1 '(d e f) '(4 5 6))) - ex-lst2 - scm> ex-lst2 - ((d e f) (4 5 6)) - scm> (get-keys-kwlist1 ex-lst2) - (d e f) - scm> (get-values-kwlist1 ex-lst2) - (4 5 6) - """, - "locked": False, - "hidden": False - }, - { - 'code': r""" - scm> (define ex-lst1 (make-kwlist2 '(a b c) '(1 2 3))) - ex-lst1 - scm> ex-lst1 - ((a 1) (b 2) (c 3)) - scm> (get-keys-kwlist2 ex-lst1) - (a b c) - scm> (get-values-kwlist2 ex-lst1) - (1 2 3) - scm> (define ex-lst2 (make-kwlist2 '(d e f) '(4 5 6))) - ex-lst2 - scm> ex-lst2 - ((d 4) (e 5) (f 6)) - scm> (get-keys-kwlist2 ex-lst2) - (d e f) - scm> (get-values-kwlist2 ex-lst2) - (4 5 6) - """, - "locked": False, - "hidden": False - }, - ], - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab11/tests/kwlist_get.py b/lab/sol-lab11/tests/kwlist_get.py deleted file mode 100644 index 07446d52d9..0000000000 --- a/lab/sol-lab11/tests/kwlist_get.py +++ /dev/null @@ -1,76 +0,0 @@ -test = { - 'name': 'kwlist-get', - 'points': 1, - 'suites': [ - { - 'type': 'scheme', - 'setup': r""" - scm> (load-all ".") ; abstraction 1 - scm> (define make-kwlist make-kwlist1) - scm> (define get-keys-kwlist get-keys-kwlist1) - scm> (define get-values-kwlist get-values-kwlist1) - """, - 'cases': [ - { - 'code': r""" - scm> (define ex-lst (make-kwlist '(a b c) '(1 2 3))) - ex-lst - scm> (get-first-from-kwlist ex-lst 'b) - 2 - scm> (get-first-from-kwlist ex-lst 'd) ; if not found, return nil - () - scm> (define ex-lst (add-to-kwlist ex-lst 'd '4)) - ex-lst - scm> (get-first-from-kwlist ex-lst 'b) - 2 - scm> (get-first-from-kwlist ex-lst 'd) - 4 - scm> (define ex-lst (add-to-kwlist ex-lst 'd '5)) - ex-lst - scm> (get-first-from-kwlist ex-lst 'b) - 2 - scm> (get-first-from-kwlist ex-lst 'd) ; return the *first* occurrence - 4 - """, - "locked": False, - "hidden": False - }, - ], - }, - { - 'type': 'scheme', - 'setup': r""" - scm> (load-all ".") ; abstraction 2 - scm> (define make-kwlist make-kwlist2) - scm> (define get-keys-kwlist get-keys-kwlist2) - scm> (define get-values-kwlist get-values-kwlist2) - """, - 'cases': [ - { - 'code': r""" - scm> (define ex-lst (make-kwlist '(a b c) '(1 2 3))) - ex-lst - scm> (get-first-from-kwlist ex-lst 'b) - 2 - scm> (get-first-from-kwlist ex-lst 'd) ; if not found, return nil - () - scm> (define ex-lst (add-to-kwlist ex-lst 'd '4)) - ex-lst - scm> (get-first-from-kwlist ex-lst 'b) - 2 - scm> (get-first-from-kwlist ex-lst 'd) - 4 - scm> (define ex-lst (add-to-kwlist ex-lst 'd '5)) - ex-lst - scm> (get-first-from-kwlist ex-lst 'b) - 2 - scm> (get-first-from-kwlist ex-lst 'd) ; return the *first* occurrence - 4 - """, - "locked": False, - "hidden": False - }, - ], - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab11/tests/pairs.py b/lab/sol-lab11/tests/pairs.py deleted file mode 100644 index d9ced64de7..0000000000 --- a/lab/sol-lab11/tests/pairs.py +++ /dev/null @@ -1,28 +0,0 @@ -test = { - 'name': 'pairs', - 'points': 1, - 'suites': [ - { - 'type': 'sqlite', - 'setup': """ - sqlite> .read lab11.sql - """, - 'cases': [ - { - 'locked': False, - 'code': r""" - sqlite> SELECT * FROM pairs; - RSF and Wheeler together have 1600 seats - Pimentel and RSF together have 1400 seats - Li Ka Shing and RSF together have 1200 seats - Pimentel and Wheeler together have 1200 seats - RSF and Stanley together have 1200 seats - Li Ka Shing and Wheeler together have 1000 seats - Morgan and RSF together have 1000 seats - Stanley and Wheeler together have 1000 seats - """, - }, - ], - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab11/tests/pow.py b/lab/sol-lab11/tests/pow.py deleted file mode 100644 index 245815702f..0000000000 --- a/lab/sol-lab11/tests/pow.py +++ /dev/null @@ -1,36 +0,0 @@ -test = { - 'name': 'pow', - 'points': 1, - 'suites': [ - { - 'type': 'scheme', - 'setup': r""" - scm> (load-all ".") - """, - 'cases': [ - { - 'code': r""" - scm> (pow-expr 2 0) - 1 - scm> (pow-expr 2 1) - (* 2 1) - scm> (pow-expr 2 5) - (* 2 (square (square (* 2 1)))) - scm> (pow-expr 2 15) - (* 2 (square (* 2 (square (* 2 (square (* 2 1))))))) - scm> (pow-expr 2 16) - (square (square (square (square (* 2 1))))) - scm> (eval (pow-expr 2 16)) - 65536 - scm> (pow-expr 3 9) - (* 3 (square (square (square (* 3 1))))) - scm> (eval (pow-expr 3 9)) - 19683 - """, - 'hidden': False, - 'locked' : False - }, - ], - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab11/tests/remaining.py b/lab/sol-lab11/tests/remaining.py deleted file mode 100644 index fbdcd6bb39..0000000000 --- a/lab/sol-lab11/tests/remaining.py +++ /dev/null @@ -1,25 +0,0 @@ -test = { - 'name': 'remaining', - 'points': 1, - 'suites': [ - { - 'type': 'sqlite', - 'setup': """ - sqlite> .read lab11.sql - """, - 'cases': [ - { - 'locked': False, - 'code': r""" - sqlite> SELECT course, remaining FROM remaining; - 10|0 - 61A|1800 - 61B|800 - 61C|540 - 70|0 - """, - }, - ], - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab11/tests/repeat-lambda.py b/lab/sol-lab11/tests/repeat-lambda.py deleted file mode 100644 index f2d068b494..0000000000 --- a/lab/sol-lab11/tests/repeat-lambda.py +++ /dev/null @@ -1,37 +0,0 @@ -test = { - 'name': 'repeat-lambda', - 'points': 1, - 'suites': [ - { - 'type': 'scheme', - 'setup': r""" - scm> (load-all ".") - """, - 'cases': [ - { - 'code': r""" - scm> (repeat (+ 2 3) (print 1)) - 1 - 1 - 1 - 1 - 1 - """, - 'hidden': False, - 'locked' : False - }, - { - 'code': r""" - scm> (repeat 2 (repeat 2 (print 'four))) - four - four - four - four - """, - 'hidden': False, - 'locked' : False - }, - ], - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab11/tests/sharing.py b/lab/sol-lab11/tests/sharing.py deleted file mode 100644 index 1ea32110f1..0000000000 --- a/lab/sol-lab11/tests/sharing.py +++ /dev/null @@ -1,24 +0,0 @@ -test = { - 'name': 'sharing', - 'points': 1, - 'suites': [ - { - 'type': 'sqlite', - 'setup': """ - sqlite> .read lab11.sql - """, - 'cases': [ - { - 'locked': False, - 'code': r""" - sqlite> SELECT * FROM sharing; - 61A|3 - 61B|2 - 61C|2 - 70|1 - """, - }, - ], - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab11/tests/wwsd-quasiquote.py b/lab/sol-lab11/tests/wwsd-quasiquote.py deleted file mode 100644 index 6aab81361c..0000000000 --- a/lab/sol-lab11/tests/wwsd-quasiquote.py +++ /dev/null @@ -1,38 +0,0 @@ -test = { - 'name': 'wwsd-quasiquote', - 'points': 0, - 'suites': [ - { - 'type': 'scheme', - 'scored': False, - 'cases': [ - { - 'code': r""" - scm> '(1 x 3) - (1 x 3) - scm> (define x 2) - x - scm> `(1 x 3) - (1 x 3) - scm> `(1 ,x 3) - (1 2 3) - scm> `(1 x ,3) - (1 x 3) - scm> `(1 (,x) 3) - (1 (2) 3) - scm> `(1 ,(+ x 2) 3) - (1 4 3) - scm> (define y 3) - y - scm> `(x ,(* y x) y) - (x 6 y) - scm> `(1 ,(cons x (list y 4)) 5) - (1 (2 3 4) 5) - """, - }, - ], - 'setup': r""" - """, - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab12/assets/big-game.png b/lab/sol-lab12/assets/big-game.png deleted file mode 100644 index 3a65150935..0000000000 Binary files a/lab/sol-lab12/assets/big-game.png and /dev/null differ diff --git a/lab/sol-lab12/assets/joins.png b/lab/sol-lab12/assets/joins.png deleted file mode 100644 index 2fb9b7b3f7..0000000000 Binary files a/lab/sol-lab12/assets/joins.png and /dev/null differ diff --git a/lab/sol-lab12/construct_check.py b/lab/sol-lab12/construct_check.py deleted file mode 100644 index bee096e7e4..0000000000 --- a/lab/sol-lab12/construct_check.py +++ /dev/null @@ -1,180 +0,0 @@ -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/lab/sol-lab12/data.sql b/lab/sol-lab12/data.sql deleted file mode 100644 index 25602fc39c..0000000000 --- a/lab/sol-lab12/data.sql +++ /dev/null @@ -1,92 +0,0 @@ -CREATE TABLE flights AS - SELECT "SFO" AS departure, "LAX" AS arrival, 97 AS price UNION - SELECT "SFO" , "AUH" , 848 UNION - SELECT "LAX" , "SLC" , 115 UNION - SELECT "SFO" , "PDX" , 192 UNION - SELECT "AUH" , "SEA" , 932 UNION - SELECT "SLC" , "PDX" , 79 UNION - SELECT "SFO" , "LAS" , 40 UNION - SELECT "SLC" , "LAX" , 117 UNION - SELECT "SEA" , "PDX" , 32 UNION - SELECT "SLC" , "SEA" , 42 UNION - SELECT "SFO" , "SLC" , 97 UNION - SELECT "LAS" , "SLC" , 50 UNION - SELECT "LAX" , "PDX" , 89 ; - -CREATE TABLE supermarket AS - SELECT "turkey" AS item, 30 AS price UNION - SELECT "tofurky" , 20 UNION - SELECT "cornbread" , 12 UNION - SELECT "potatoes" , 10 UNION - SELECT "cranberries" , 7 UNION - SELECT "pumpkin pie" , 15 UNION - SELECT "CAKE!" , 60 UNION - SELECT "foie gras" , 70 ; - -CREATE TABLE main_course AS - SELECT "turkey" AS meat, "cranberries" AS side, 2000 AS calories UNION - SELECT "turducken" , "potatoes" , 4000 UNION - SELECT "tofurky" , "cranberries" , 1000 UNION - SELECT "tofurky" , "stuffing" , 1000 UNION - SELECT "tofurky" , "yams" , 1000 UNION - SELECT "turducken" , "turducken" , 9000 UNION - SELECT "turkey" , "potatoes" , 2000 UNION - SELECT "turkey" , "bread" , 1500 UNION - SELECT "tofurky" , "soup" , 1200 UNION - SELECT "chicken" , "cranberries" , 2500 UNION - SELECT "turducken" , "butter" , 10000 UNION - SELECT "turducken" , "more_butter" , 15000 ; - -CREATE TABLE pies AS - SELECT "pumpkin" AS pie, 500 AS calories UNION - SELECT "apple" , 400 UNION - SELECT "chocolate" , 600 UNION - SELECT "cherry" , 550 ; - -CREATE TABLE products AS - SELECT "phone" AS category, "uPhone" AS name, 99.99 AS MSRP, 4.5 AS rating UNION - SELECT "phone" , "rPhone" , 79.99 , 3 UNION - SELECT "phone" , "qPhone" , 89.99 , 4 UNION - SELECT "games" , "GameStation" , 299.99 , 3 UNION - SELECT "games" , "QBox" , 399.99 , 3.5 UNION - SELECT "computer" , "iBook" , 112.99 , 4 UNION - SELECT "computer" , "wBook" , 114.29 , 4.4 UNION - SELECT "computer" , "kBook" , 99.99 , 3.8 ; - -CREATE TABLE inventory AS - SELECT "Hallmart" AS store, "uPhone" AS item, 99.99 AS price UNION - SELECT "Targive" , "uPhone" , 100.99 UNION - SELECT "RestBuy" , "uPhone" , 89.99 UNION - - SELECT "Hallmart" , "rPhone" , 69.99 UNION - SELECT "Targive" , "rPhone" , 79.99 UNION - SELECT "RestBuy" , "rPhone" , 75.99 UNION - - SELECT "Hallmart" , "qPhone" , 85.99 UNION - SELECT "Targive" , "qPhone" , 88.98 UNION - SELECT "RestBuy" , "qPhone" , 87.98 UNION - - SELECT "Hallmart" , "GameStation" , 298.98 UNION - SELECT "Targive" , "GameStation" , 300.98 UNION - SELECT "RestBuy" , "GameStation" , 310.99 UNION - - SELECT "Hallmart" , "QBox" , 399.99 UNION - SELECT "Targive" , "QBox" , 390.98 UNION - SELECT "RestBuy" , "QBox" , 410.98 UNION - - SELECT "Hallmart" , "iBook" , 111.99 UNION - SELECT "Targive" , "iBook" , 110.99 UNION - SELECT "RestBuy" , "iBook" , 112.99 UNION - - SELECT "Hallmart" , "wBook" , 117.29 UNION - SELECT "Targive" , "wBook" , 119.29 UNION - SELECT "RestBuy" , "wBook" , 114.29 UNION - - SELECT "Hallmart" , "kBook" , 95.99 UNION - SELECT "Targive" , "kBook" , 96.99 UNION - SELECT "RestBuy" , "kBook" , 94.99 ; - -CREATE TABLE stores AS - SELECT "Hallmart" AS store, "50 Lawton Way" AS address, 25 AS Mbs UNION - SELECT "Targive" , "2 Red Circle Way" , 40 UNION - SELECT "RestBuy" , "1 Kiosk Ave" , 30 ; \ No newline at end of file diff --git a/lab/sol-lab12/index.html b/lab/sol-lab12/index.html deleted file mode 100644 index 3ca6f2f872..0000000000 --- a/lab/sol-lab12/index.html +++ /dev/null @@ -1,876 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Lab 12 Solutions | CS 61A Fall 2024 - - - - - - -
- -
-
-
-

- -Lab 12 Solutions - - - - - - -

-
- -

Solution Files

- - - -

Required Questions

- - - - - -

SQL

- - -

A SELECT statement describes an output table based on input rows. To write one:

- -
    -
  1. Describe the input rows using FROM and WHERE clauses.
  2. -
  3. Group those rows and determine which groups should appear as output rows using GROUP BY and HAVING clauses.
  4. -
  5. Format and order the output rows and columns using SELECT and ORDER BY clauses.
  6. -
- -

SELECT (Step 3) FROM (Step 1) WHERE (Step 1) GROUP BY (Step 2) HAVING (Step 2) ORDER BY (Step 3);

- -

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.

- -

Consult the drop-down for a refresher on SQL. It's okay to skip directly to the questions and refer back here should you get stuck. -

- - - -
- - -

SQL Basics

- - - -

Creating Tables

- - -

You can create SQL tables either from scratch or from existing tables.

- -

The following statement creates a table by specifying column names and values -without referencing another table. Each SELECT clause specifies the values -for one row, and UNION is used to join rows together. The AS clauses give a -name to each column; it need not be repeated in subsequent rows after the -first.

- -
CREATE TABLE [table_name] AS
-  SELECT [val1] AS [column1], [val2] AS [column2], ... UNION
-  SELECT [val3]             , [val4]             , ... UNION
-  SELECT [val5]             , [val6]             , ...;
- -

Let's say we want to make the following table called big_game which records -the scores for the Big Game each year. This table has three columns: -berkeley, stanford, and year.

- - - -

We could do so with the following CREATE TABLE statement:

- -
CREATE TABLE big_game AS
-  SELECT 30 AS berkeley, 7 AS stanford, 2002 AS year UNION
-  SELECT 28,             16,            2003         UNION
-  SELECT 17,             38,            2014;
- - -

Selecting From Tables

- -

More commonly, we will create new tables by selecting specific columns that we -want from existing tables by using a SELECT statement as follows:

- -
SELECT [columns] FROM [tables] WHERE [condition] ORDER BY [columns] LIMIT [limit];
- -

Let's break down this statement:

- -
    -
  • SELECT [columns] tells SQL that we want to include the given columns in our - output table; [columns] is a comma-separated list of column names, and * - can be used to select all columns
  • -
  • FROM [table] tells SQL that the columns we want to select are from the - given table; see the joins section to see how to select - from multiple tables
  • -
  • WHERE [condition] filters the output table by only including rows whose - values satisfy the given [condition], a boolean expression
  • -
  • ORDER BY [columns] orders the rows in the output table by the given - comma-separated list of columns
  • -
  • LIMIT [limit] limits the number of rows in the output table by the integer - [limit]
  • -
- -

- -

Here are some examples:

- -

Select all of Berkeley's scores from the big_game table, but only include -scores from years past 2002:

- -
sqlite> SELECT berkeley FROM big_game WHERE year > 2002;
-28
-17
- -

Select the scores for both schools in years that Berkeley won:

- -
sqlite> SELECT berkeley, stanford FROM big_game WHERE berkeley > stanford;
-30|7
-28|16
- -

Select the years that Stanford scored more than 15 points:

- -
sqlite> SELECT year FROM big_game WHERE stanford > 15;
-2003
-2014
- - -

SQL operators

- - -

Expressions in the SELECT, WHERE, and ORDER BY clauses can contain -one or more of the following operators:

- -
    -
  • comparison operators: =, >, <, <=, >=, <> or != ("not equal")
  • -
  • boolean operators: AND, OR
  • -
  • arithmetic operators: +, -, *, /
  • -
  • concatenation operator: ||
  • -
- -

Output the ratio of Berkeley's score to Stanford's score each year:

- -
sqlite> select berkeley * 1.0 / stanford from big_game;
-0.447368421052632
-1.75
-4.28571428571429
- -

Output the sum of scores in years where both teams scored over 10 points:

- -
sqlite> select berkeley + stanford from big_game where berkeley > 10 and stanford > 10;
-55
-44
- -

Output a table with a single column and single row containing the value "hello -world":

- -
sqlite> SELECT "hello" || " " || "world";
-hello world
- - -

Joins

- - -

To select data from multiple tables, we can use joins. There are many types -of joins, but the only one we'll worry about is the inner join. To perform -an inner join on two on more tables, simply list them all out in the FROM -clause of a SELECT statement:

- -
SELECT [columns] FROM [table1], [table2], ... WHERE [condition] ORDER BY [columns] LIMIT [limit];
- -

We can select from multiple different tables or from the same table multiple -times.

- -

Let's say we have the following table that contains the names of head football -coaches at Cal since 2002:

- -
CREATE TABLE coaches AS
-  SELECT "Jeff Tedford" AS name, 2002 as start, 2012 as end UNION
-  SELECT "Sonny Dykes"         , 2013         , 2016        UNION
-  SELECT "Justin Wilcox"       , 2017         , null;
- -

When we join two or more tables, the default output is a cartesian product. For -example, if we joined big_game with coaches, we'd get the following:

- - - -

If we want to match up each game with the coach that season, we'd have to -compare columns from the two tables in the WHERE clause:

- -
sqlite> SELECT * FROM big_game, coaches WHERE year >= start AND year <= end;
-17|38|2014|Sonny Dykes|2013|2016
-28|16|2003|Jeff Tedford|2002|2012
-30|7|2002|Jeff Tedford|2002|2012
- -

The following query outputs the coach and year for each Big Game win recorded -in big_game:

- -
sqlite> SELECT name, year FROM big_game, coaches
-...>        WHERE berkeley > stanford AND year >= start AND year <= end;
-Jeff Tedford|2003
-Jeff Tedford|2002
- -

In the queries above, none of the column names are ambiguous. For example, it -is clear that the name column comes from the coaches table because there -isn't a column in the big_game table with that name. However, if a column -name exists in more than one of the tables being joined, or if we join a table -with itself, we must disambiguate the column names using aliases.

- -

For examples, let's find out what the score difference is for each team between -a game in big_game and any previous games. Since each row in this table represents -one game, in order to compare two games we must join big_game with itself:

- -
sqlite> SELECT b.Berkeley - a.Berkeley, b.Stanford - a.Stanford, a.Year, b.Year
-...>        FROM big_game AS a, big_game AS b WHERE a.Year < b.Year;
--11|22|2003|2014
--13|21|2002|2014
--2|9|2002|2003
- -

In the query above, we give the alias a to the first big_game table and the -alias b to the second big_game table. We can then reference columns from -each table using dot notation with the aliases, e.g. a.Berkeley, -a.Stanford, and a.Year to select from the first table.

- - -

SQL Aggregation

- - -

Previously, we have been dealing with queries that process one row at a time. -When we join, we make pairwise combinations of all of the rows. When we use -WHERE, we filter out certain rows based on the condition. Alternatively, -applying an aggregate function -such as MAX(column) combines the values in multiple rows.

- -

By default, we combine the values of the entire table. For example, if we -wanted to count the number of flights from our flights table, we could use:

- -
sqlite> SELECT COUNT(*) from FLIGHTS;
-13
- -

What if we wanted to group together the values in similar rows and perform the -aggregation operations within those groups? We use a GROUP BY clause.

- -

Here's another example. For each unique departure, collect all the rows having -the same departure airport into a group. Then, select the price column and -apply the MIN aggregation to recover the price of the cheapest departure from -that group. The end result is a table of departure airports and the cheapest -departing flight.

- -
sqlite> SELECT departure, MIN(price) FROM flights GROUP BY departure;
-AUH|932
-LAS|50
-LAX|89
-SEA|32
-SFO|40
-SLC|42
- -

Just like how we can filter out rows with WHERE, we can also filter out -groups with HAVING. Typically, a HAVING clause should use an aggregation -function. Suppose we want to see all airports with at least two departures:

- -
sqlite> SELECT departure FROM flights GROUP BY departure HAVING COUNT(*) >= 2;
-LAX
-SFO
-SLC
- -

Note that the COUNT(*) aggregate just counts the number of rows in each group. -Say we want to count the number of distinct airports instead. Then, we could -use the following query:

- -
sqlite> SELECT COUNT(DISTINCT departure) FROM flights;
-6
- -

This enumerates all the different departure airports available in our flights -table (in this case: SFO, LAX, AUH, SLC, SEA, and LAS).

- - -

Usage

- - -

First, check that a file named sqlite_shell.py exists alongside the assignment files. -If you don't see it, or if you encounter problems with it, scroll down to the Troubleshooting -section to see how to download an official precompiled SQLite binary before proceeding.

- -

You can start an interactive SQLite session in your Terminal or Git Bash with -the following command:

- -
python3 sqlite_shell.py
- -

While the interpreter is running, you can type .help to see some of the -commands you can run.

- -

To exit out of the SQLite interpreter, type .exit or .quit or press -Ctrl-C. Remember that if you see ...> after pressing enter, you probably -forgot a ;.

- -

You can also run all the statements in a .sql file by doing the following: -(Here we're using the lab13.sql file as an example.)

- -
    -
  1. Runs your code and then exits SQLite immediately afterwards.

    - -
    python3 sqlite_shell.py < lab13.sql
  2. -
  3. Runs your code and then opens an interactive SQLite session, which is - similar to running Python code with the interactive -i flag.

    - -
    python3 sqlite_shell.py --init lab13.sql
  4. -
- -
- -
- - -

Final Exam Rooms

- - -

The finals table has columns hall (strings) and course (strings), and has -rows for the lecture halls in which a course is holding its final exam.

- -

The sizes table has columns room (strings) and seats (numbers), and has one -row per unique room on campus containing the number of seats in that room. All -lecture halls are rooms.

- - - -
CREATE TABLE finals AS
-  SELECT "RSF" AS hall, "61A" as course UNION
-  SELECT "Wheeler"    , "61A"           UNION
-  SELECT "Pimentel"   , "61A"           UNION
-  SELECT "Li Ka Shing", "61A"           UNION
-  SELECT "Stanley"    , "61A"           UNION
-  SELECT "RSF"        , "61B"           UNION
-  SELECT "Wheeler"    , "61B"           UNION
-  SELECT "Morgan"     , "61B"           UNION
-  SELECT "Wheeler"    , "61C"           UNION
-  SELECT "Pimentel"   , "61C"           UNION
-  SELECT "Soda 310"   , "61C"           UNION
-  SELECT "Soda 306"   , "10"            UNION
-  SELECT "RSF"        , "70";
-
-CREATE TABLE sizes AS
-  SELECT "RSF" AS room, 900 as seats    UNION
-  SELECT "Wheeler"    , 700             UNION
-  SELECT "Pimentel"   , 500             UNION
-  SELECT "Li Ka Shing", 300             UNION
-  SELECT "Stanley"    , 300             UNION
-  SELECT "Morgan"     , 100             UNION
-  SELECT "Soda 306"   , 80              UNION
-  SELECT "Soda 310"   , 40              UNION
-  SELECT "Soda 320"   , 30;
- -
- - -

Q1: Room Sharing

- - -

Create a sharing table with two columns, course (strings) and shared -(numbers), that has a row for each course using at least one room that is also -used by another course. Each row contains the name of the course and the total -number of rooms for that course which are also used by another course.

- -

Reminder: COUNT(DISTINCT x) evaluates to the number of distinct values -that appear in column x for a group.

- -

Your query should work correctly for any data that might appear in the finals -and sizes table, but for the example above the result should be:

- -
61A|3
-61B|2
-61C|2
-70|1
- - - - - -
- -
SELECT _.course, COUNT(DISTINCT _____) AS shared
-  FROM finals AS a, finals AS b
-  WHERE _____ GROUP BY _____;
- - - -
    -
  1. Use FROM and WHERE to create a row for each instance of two courses sharing a final room.
  2. -
  3. Use GROUP BY to create one group for each course.
  4. -
  5. Use SELECT to compute the total number of rooms for that course which are also used by another course -
- - - - -
CREATE TABLE sharing AS
-
SELECT a.course, COUNT(DISTINCT a.hall) AS shared - FROM finals AS a, finals AS b WHERE a.hall = b.hall AND a.course != b.course - GROUP BY a.course;
- -
Use Ok to test your code:

python3 ok -q sharing
- -
- - -

Q2: Two Rooms

- - -

Create a pairs table with one column rooms (strings) that contains sentences -describing pairs of rooms that together have at least 1,000 seats, along with -the number of seats they have. The room names should appear in alphabetical -order. Rows should appear in decreasing order of the total seats in the pair of -rooms.

- -

Your query should work correctly for any data that might appear in the finals -and sizes table, but for the example above the result should be:

- -

Hint: When adding numbers and including the result in a string, put parentheses around the arithmetic: "1 + 2 = " || (1 + 2)

- -
RSF and Wheeler together have 1600 seats
-Pimentel and RSF together have 1400 seats
-Li Ka Shing and RSF together have 1200 seats
-Pimentel and Wheeler together have 1200 seats
-RSF and Stanley together have 1200 seats
-Li Ka Shing and Wheeler together have 1000 seats
-Morgan and RSF together have 1000 seats
-Stanley and Wheeler together have 1000 seats
- - - - - -
- -
  SELECT _____ || " and " || _____ || " together have " || (_____) || " seats" AS rooms
-    FROM sizes AS a, sizes AS b WHERE _____
-    ORDER BY _____ DESC;
- - - -
    -
  1. Use FROM and WHERE to create a row for each pair of different rooms (in alphabetical order) with at least 1,000 seats total.
  2. -
  3. No grouping is needed
  4. -
  5. Use SELECT to compute the total number of rooms for that course which are also used by another course -
- - - - -
CREATE TABLE pairs AS
-
SELECT a.room || " and " || b.room || " together have " || (a.seats + b.seats) || " seats" AS rooms - FROM sizes AS a, sizes AS b WHERE a.room < b.room AND a.seats + b.seats >= 1000 - ORDER BY a.seats + b.seats DESC;
- -
Use Ok to test your code:

python3 ok -q pairs
- -
- - -

Q3: (OPTIONAL) Big Courses

- - -

Create a big table with one column course (strings) containing the names -of the courses (one per row) that have at least 1,000 seats in their final exam.

- -

Your query should work correctly for any data that might appear in the finals -and sizes table, but for the example above the result should be:

- -
61A
-61B
-61C
- - - - - -
- -
SELECT _____ FROM _____ WHERE _____ GROUP BY _____ HAVING _____;
- - - -
    -
  1. Use FROM and WHERE to combine the information in the finals and sizes tables.
  2. -
  3. Use GROUP BY and HAVING to create one group for each course that has at least 1,000 seats.
  4. -
  5. Use SELECT to put the name of the course in the output. -
- - - - -
CREATE TABLE big AS
-
SELECT course FROM finals, sizes WHERE hall=room GROUP BY course HAVING SUM(seats) >= 1000;
- -
Use Ok to test your code:

python3 ok -q big
- -
- - -

Q4: (OPTIONAL) Seats Remaining

- - -

Create a remaining table with two columns, course (strings) and remaining -(numbers), that has a row for each course. Each row contains the name of the -course and the total number of seats in all final rooms for that course except -the largest one.

- -

Your query should work correctly for any data that might appear in the finals -and sizes table, but for the example above the result should be:

- -
10|0
-61A|1800
-61B|800
-61C|540
-70|0
- - - - - -
- -
SELECT course, _____ AS remaining
-  FROM _____ WHERE _____ GROUP BY _____;
- - - -
    -
  1. Use FROM and WHERE to combine the information in the finals and sizes tables.
  2. -
  3. Use GROUP BY to create one group for each course.
  4. -
  5. Use SELECT to compute the total number of seats in all final rooms for that course except the largest one. -
- - - - -
CREATE TABLE remaining AS
-
SELECT course, SUM(seats) - MAX(seats) AS remaining - FROM finals, sizes WHERE hall=room GROUP BY course;
- -
- -

Use Ok to test your code:

python3 ok -q remaining
- -
- - -

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.

- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/lab/sol-lab12/lab12.ok b/lab/sol-lab12/lab12.ok deleted file mode 100644 index 48c13fd696..0000000000 --- a/lab/sol-lab12/lab12.ok +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "Lab 12", - "endpoint": "cal/cs61a/fa24/lab12", - "src": [ - "lab12.sql" - ], - "tests": { - "lab*.py": "doctest", - "tests/*.py": "ok_test" - }, - "default_tests": [], - "protocols": [ - "restore", - "file_contents", - "unlock", - "grading", - "analytics", - "backup" - ] -} \ No newline at end of file diff --git a/lab/sol-lab12/lab12.py b/lab/sol-lab12/lab12.py deleted file mode 100644 index a3e14f4a45..0000000000 --- a/lab/sol-lab12/lab12.py +++ /dev/null @@ -1,361 +0,0 @@ -def insert_into_all(item, nested_list): - """Return a new list consisting of all the lists in nested_list, - but with item added to the front of each. You can assume that - nested_list is a list of lists. - - >>> nl = [[], [1, 2], [3]] - >>> insert_into_all(0, nl) - [[0], [0, 1, 2], [0, 3]] - """ - return [[item] + lst for lst in nested_list] - -def subseqs(s): - """Return a nested list (a list of lists) of all subsequences of S. - The subsequences can appear in any order. You can assume S is a list. - - >>> seqs = subseqs([1, 2, 3]) - >>> sorted(seqs) - [[], [1], [1, 2], [1, 2, 3], [1, 3], [2], [2, 3], [3]] - >>> subseqs([]) - [[]] - """ - if not s: - return [[]] - else: - subset = subseqs(s[1:]) - return insert_into_all(s[0], subset) + subset - - -def non_decrease_subseqs(s): - """Assuming that S is a list, return a nested list of all subsequences - of S (a list of lists) for which the elements of the subsequence - are strictly nondecreasing. The subsequences can appear in any order. - - >>> seqs = non_decrease_subseqs([1, 3, 2]) - >>> sorted(seqs) - [[], [1], [1, 2], [1, 3], [2], [3]] - >>> non_decrease_subseqs([]) - [[]] - >>> seqs2 = non_decrease_subseqs([1, 1, 2]) - >>> sorted(seqs2) - [[], [1], [1], [1, 1], [1, 1, 2], [1, 2], [1, 2], [2]] - """ - def subseq_helper(s, prev): - if not s: - return [[]] - elif s[0] < prev: - return subseq_helper(s[1:], prev) - else: - a = subseq_helper(s[1:], s[0]) - b = subseq_helper(s[1:], prev) - return insert_into_all(s[0], a) + b - return subseq_helper(s, 0) - - -def num_trees(n): - """Returns the number of unique full binary trees with exactly n leaves. E.g., - - 1 2 3 3 ... - * * * * - / \ / \ / \ - * * * * * * - / \ / \ - * * * * - - >>> num_trees(1) - 1 - >>> num_trees(2) - 1 - >>> num_trees(3) - 2 - >>> num_trees(8) - 429 - - """ - if n == 1: - return 1 - return sum(num_trees(k) * num_trees(n-k) for k in range(1, n)) - - -def partition_gen(n): - """ - >>> partitions = [sorted(p) for p in partition_gen(4)] - >>> for partition in sorted(partitions): # note: order doesn't matter - ... print(partition) - [1, 1, 1, 1] - [1, 1, 2] - [1, 3] - [2, 2] - [4] - """ - def yield_helper(num, segment): - if num == 0: - yield [] - elif num > 0 and segment > 0: - for small_part in yield_helper(num - segment, segment): - yield [segment] + small_part - yield from yield_helper(num, segment - 1) - yield from yield_helper(n, n) - - -class CucumberGame: - """Play a round and return all winners so far. Cards is a list of pairs. - Each (who, card) pair in cards indicates who plays and what card they play. - >>> g = CucumberGame() - >>> g.play_round(3, [(3, 4), (0, 8), (1, 8), (2, 5)]) - >>> g.winners - [1] - >>> g.play_round(1, [(3, 5), (1, 4), (2, 5), (0, 8), (3, 7), (0, 6), (1, 7)]) - It is not your turn, player 3 - It is not your turn, player 0 - The round is over, player 1 - >>> g.winners - [1, 3] - >>> g.play_round(3, [(3, 7), (2, 5), (0, 9)]) # Round is never completed - It is not your turn, player 2 - >>> g.winners - [1, 3] - """ - def __init__(self): - self.winners = [] - - def play_round(self, starter, cards): - r = Round(starter) - for who, card in cards: - try: - r.play(who, card) - except AssertionError as e: - print(e) - if r.winner != None: - self.winners.append(r.winner) - - -class Round: - players = 4 - - def __init__(self, starter): - self.starter = starter - self.next_player = starter - self.highest = -1 - self.winner = None - - def play(self, who, card): - assert not self.is_complete(), f'The round is over, player {who}' - assert who == self.next_player, f'It is not your turn, player {who}' - self.next_player = (who + 1) % Round.players - if card >= self.highest: - self.highest = card - self.control = who - if self.is_complete(): - self.winner = self.control - - def is_complete(self): - """ Checks if a game could end. """ - return self.next_player == self.starter and self.highest > -1 - - -def trade(first, second): - """Exchange the smallest prefixes of first and second that have equal sum. - - >>> a = [1, 1, 3, 2, 1, 1, 4] - >>> b = [4, 3, 2, 7] - >>> trade(a, b) # Trades 1+1+3+2=7 for 4+3=7 - 'Deal!' - >>> a - [4, 3, 1, 1, 4] - >>> b - [1, 1, 3, 2, 2, 7] - >>> c = [3, 3, 2, 4, 1] - >>> trade(b, c) - 'No deal!' - >>> b - [1, 1, 3, 2, 2, 7] - >>> c - [3, 3, 2, 4, 1] - >>> trade(a, c) - 'Deal!' - >>> a - [3, 3, 2, 1, 4] - >>> b - [1, 1, 3, 2, 2, 7] - >>> c - [4, 3, 1, 4, 1] - >>> d = [1, 1] - >>> e = [2] - >>> trade(d, e) - 'Deal!' - >>> d - [2] - >>> e - [1, 1] - """ - m, n = 1, 1 - - equal_prefix = lambda: sum(first[:m]) == sum(second[:n]) - while m <= len(first) and n <= len(second) and not equal_prefix(): - if sum(first[:m]) < sum(second[:n]): - m += 1 - else: - n += 1 - - if equal_prefix(): - first[:m], second[:n] = second[:n], first[:m] - return 'Deal!' - else: - return 'No deal!' - - -def card(n): - """Return the playing card numeral as a string for a positive n <= 13.""" - assert type(n) == int and n > 0 and n <= 13, "Bad card n" - specials = {1: 'A', 11: 'J', 12: 'Q', 13: 'K'} - return specials.get(n, str(n)) - -def shuffle(cards): - """Return a shuffled list that interleaves the two halves of cards. - - >>> shuffle(range(6)) - [0, 3, 1, 4, 2, 5] - >>> suits = ['H', 'D', 'S', 'C'] - >>> cards = [card(n) + suit for n in range(1,14) for suit in suits] - >>> cards[:12] - ['AH', 'AD', 'AS', 'AC', '2H', '2D', '2S', '2C', '3H', '3D', '3S', '3C'] - >>> cards[26:30] - ['7S', '7C', '8H', '8D'] - >>> shuffle(cards)[:12] - ['AH', '7S', 'AD', '7C', 'AS', '8H', 'AC', '8D', '2H', '8S', '2D', '8C'] - >>> shuffle(shuffle(cards))[:12] - ['AH', '4D', '7S', '10C', 'AD', '4S', '7C', 'JH', 'AS', '4C', '8H', 'JD'] - >>> cards[:12] # Should not be changed - ['AH', 'AD', 'AS', 'AC', '2H', '2D', '2S', '2C', '3H', '3D', '3S', '3C'] - """ - assert len(cards) % 2 == 0, 'len(cards) must be even' - half = len(cards) // 2 - shuffled = [] - for i in range(half): - shuffled.append(cards[i]) - shuffled.append(cards[half+i]) - return shuffled - - -def link_pop(lnk, index=-1): - '''Implement the pop method for a Linked List. - - >>> lnk = Link(1, Link(2, Link(3, Link(4, Link(5))))) - >>> removed = link_pop(lnk) - >>> print(removed) - 5 - >>> print(lnk) - <1 2 3 4> - >>> link_pop(lnk, 2) - 3 - >>> print(lnk) - <1 2 4> - >>> link_pop(lnk) - 4 - >>> link_pop(lnk) - 2 - >>> print(lnk) - <1> - ''' - if index == -1: - while lnk.rest.rest is not Link.empty: - lnk = lnk.rest - removed = lnk.rest.first - lnk.rest = Link.empty - else: - while index > 1: - lnk = lnk.rest - index -= 1 - removed = lnk.rest.first - lnk.rest = lnk.rest.rest - return removed - - - -def deep_len(lnk): - """ Returns the deep length of a possibly deep linked list. - - >>> deep_len(Link(1, Link(2, Link(3)))) - 3 - >>> deep_len(Link(Link(1, Link(2)), Link(3, Link(4)))) - 4 - >>> levels = Link(Link(Link(1, Link(2)), Link(3)), Link(Link(4), Link(5))) - >>> print(levels) - <<<1 2> 3> <4> 5> - >>> deep_len(levels) - 5 - """ - if lnk is Link.empty: - return 0 - elif not isinstance(lnk, Link): - return 1 - else: - return deep_len(lnk.first) + deep_len(lnk.rest) - - -def every_other(s): - """Mutates a linked list so that all the odd-indiced elements are removed - (using 0-based indexing). - - >>> s = Link(1, Link(2, Link(3, Link(4)))) - >>> every_other(s) - >>> s - Link(1, Link(3)) - >>> odd_length = Link(5, Link(3, Link(1))) - >>> every_other(odd_length) - >>> odd_length - Link(5, Link(1)) - >>> singleton = Link(4) - >>> every_other(singleton) - >>> singleton - Link(4) - """ - if s is Link.empty or s.rest is Link.empty: - return - else: - s.rest = s.rest.rest - every_other(s.rest) - - -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/lab/sol-lab12/lab12.scm b/lab/sol-lab12/lab12.scm deleted file mode 100644 index b54f64ea2e..0000000000 --- a/lab/sol-lab12/lab12.scm +++ /dev/null @@ -1,19 +0,0 @@ -(define (no-repeats s) - (if (null? s) - s - (cons (car s) - (no-repeats - (filter (lambda (x) (not (= (car s) x))) (cdr s)))))) - -(define (without-duplicates lst) - (if (null? lst) - lst - (cons (car lst) - (without-duplicates - (filter (lambda (x) (not (= (car lst) x))) - (cdr lst)))))) - -(define (reverse lst) - (if (null? lst) - nil - (append (reverse (cdr lst)) (list (car lst))))) diff --git a/lab/sol-lab12/lab12.sql b/lab/sol-lab12/lab12.sql deleted file mode 100644 index 47d874a9d1..0000000000 --- a/lab/sol-lab12/lab12.sql +++ /dev/null @@ -1,43 +0,0 @@ -CREATE TABLE finals AS - SELECT "RSF" AS hall, "61A" as course UNION - SELECT "Wheeler" , "61A" UNION - SELECT "Pimentel" , "61A" UNION - SELECT "Li Ka Shing", "61A" UNION - SELECT "Stanley" , "61A" UNION - SELECT "RSF" , "61B" UNION - SELECT "Wheeler" , "61B" UNION - SELECT "Morgan" , "61B" UNION - SELECT "Wheeler" , "61C" UNION - SELECT "Pimentel" , "61C" UNION - SELECT "Soda 310" , "61C" UNION - SELECT "Soda 306" , "10" UNION - SELECT "RSF" , "70"; - -CREATE TABLE sizes AS - SELECT "RSF" AS room, 900 as seats UNION - SELECT "Wheeler" , 700 UNION - SELECT "Pimentel" , 500 UNION - SELECT "Li Ka Shing", 300 UNION - SELECT "Stanley" , 300 UNION - SELECT "Morgan" , 100 UNION - SELECT "Soda 306" , 80 UNION - SELECT "Soda 310" , 40 UNION - SELECT "Soda 320" , 30; - -CREATE TABLE sharing AS - SELECT a.course, COUNT(DISTINCT a.hall) AS shared - FROM finals AS a, finals AS b WHERE a.hall = b.hall AND a.course != b.course - GROUP BY a.course; - -CREATE TABLE pairs AS - SELECT a.room || " and " || b.room || " together have " || (a.seats + b.seats) || " seats" AS rooms - FROM sizes AS a, sizes AS b WHERE a.room < b.room AND a.seats + b.seats >= 1000 - ORDER BY a.seats + b.seats DESC; - -CREATE TABLE big AS - SELECT course FROM finals, sizes WHERE hall=room GROUP BY course HAVING SUM(seats) >= 1000; - -CREATE TABLE remaining AS - SELECT course, SUM(seats) - MAX(seats) AS remaining - FROM finals, sizes WHERE hall=room GROUP BY course; - diff --git a/lab/sol-lab12/lab12.zip b/lab/sol-lab12/lab12.zip deleted file mode 100644 index 12d5e965b6..0000000000 Binary files a/lab/sol-lab12/lab12.zip and /dev/null differ diff --git a/lab/sol-lab12/link_class.py b/lab/sol-lab12/link_class.py deleted file mode 100644 index a343c8b83a..0000000000 --- a/lab/sol-lab12/link_class.py +++ /dev/null @@ -1,40 +0,0 @@ -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) + '>' \ No newline at end of file diff --git a/lab/sol-lab12/ok b/lab/sol-lab12/ok deleted file mode 100644 index 0d2fead7a3..0000000000 Binary files a/lab/sol-lab12/ok and /dev/null differ diff --git a/lab/sol-lab12/scheme b/lab/sol-lab12/scheme deleted file mode 100644 index 346c9b5fa7..0000000000 Binary files a/lab/sol-lab12/scheme and /dev/null differ diff --git a/lab/sol-lab12/sqlite_shell.py b/lab/sol-lab12/sqlite_shell.py deleted file mode 100644 index cf1d3686fc..0000000000 --- a/lab/sol-lab12/sqlite_shell.py +++ /dev/null @@ -1,681 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the MIT license - -# A simple SQLite shell that uses the built-in Python adapter. - -import codecs -import io -import os -import sys -import sqlite3 -import time -import warnings - -try: FileNotFoundError -except NameError: FileNotFoundError = OSError - -if str != bytes: buffer = bytes -if str != bytes: unicode = str - -try: import msvcrt -except ImportError: msvcrt = None - -CP_UTF8 = 65001 -pythonapi = None -if msvcrt: - import ctypes - (BOOL, DWORD, HANDLE, UINT) = (ctypes.c_long, ctypes.c_ulong, ctypes.c_void_p, ctypes.c_uint) - GetConsoleCP = ctypes.WINFUNCTYPE(UINT)(('GetConsoleCP', ctypes.windll.kernel32)) - SetConsoleCP = ctypes.WINFUNCTYPE(BOOL, UINT)(('SetConsoleCP', ctypes.windll.kernel32)) - GetConsoleOutputCP = ctypes.WINFUNCTYPE(UINT)(('GetConsoleOutputCP', ctypes.windll.kernel32)) - SetConsoleOutputCP = ctypes.WINFUNCTYPE(BOOL, UINT)(('SetConsoleOutputCP', ctypes.windll.kernel32)) - GetConsoleMode = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.POINTER(DWORD), use_last_error=True)(('GetConsoleMode', ctypes.windll.kernel32)) - GetNumberOfConsoleInputEvents = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.POINTER(DWORD), use_last_error=True)(('GetNumberOfConsoleInputEvents', ctypes.windll.kernel32)) - ReadConsoleW = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.c_void_p, DWORD, ctypes.POINTER(DWORD), ctypes.c_void_p, use_last_error=True)(('ReadConsoleW', ctypes.windll.kernel32)) - WriteConsoleW = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.c_void_p, DWORD, ctypes.POINTER(DWORD), ctypes.c_void_p, use_last_error=True)(('WriteConsoleW', ctypes.windll.kernel32)) - class Py_buffer(ctypes.Structure): _fields_ = [('buf', ctypes.c_void_p), ('obj', ctypes.py_object), ('len', ctypes.c_ssize_t), ('itemsize', ctypes.c_ssize_t), ('readonly', ctypes.c_int), ('ndim', ctypes.c_int), ('format', ctypes.c_char_p), ('shape', ctypes.POINTER(ctypes.c_ssize_t)), ('strides', ctypes.POINTER(ctypes.c_ssize_t)), ('suboffsets', ctypes.POINTER(ctypes.c_ssize_t))] + ([('smalltable', ctypes.c_ssize_t * 2)] if sys.version_info[0] <= 2 else []) + [('internal', ctypes.c_void_p)] - try: from ctypes import pythonapi - except ImportError: pass -if pythonapi: - def getbuffer(b, writable): - arr = Py_buffer() - pythonapi.PyObject_GetBuffer(ctypes.py_object(b), ctypes.byref(arr), ctypes.c_int(writable)) - try: buf = (ctypes.c_ubyte * arr.len).from_address(arr.buf) - finally: pythonapi.PyBuffer_Release(ctypes.byref(arr)) - return buf - -ENCODING = 'utf-8' - -if sys.version_info[0] < 3: - class NotASurrogateError(Exception): pass - def surrogateescape_handler(exc): - # Source: https://github.com/PythonCharmers/python-future/blob/aef57391c0cd58bf840dff5e2bc2c8c0f5b0a1b4/src/future/utils/surrogateescape.py - mystring = exc.object[exc.start:exc.end] - try: - if isinstance(exc, UnicodeDecodeError): - decoded = [] - for ch in mystring: - if isinstance(ch, int): - code = ch - else: - code = ord(ch) - if 0x80 <= code <= 0xFF: - decoded.append(unichr(0xDC00 + code)) - elif code <= 0x7F: - decoded.append(unichr(code)) - else: - raise NotASurrogateError() - decoded = str().join(decoded) - elif isinstance(exc, UnicodeEncodeError): - decoded = [] - for ch in mystring: - code = ord(ch) - if not 0xD800 <= code <= 0xDCFF: - raise NotASurrogateError() - if 0xDC00 <= code <= 0xDC7F: - decoded.append(unichr(code - 0xDC00)) - elif code <= 0xDCFF: - decoded.append(unichr(code - 0xDC00)) - else: - raise NotASurrogateError() - decoded = str().join(decoded) - else: - raise exc - except NotASurrogateError: - raise exc - return (decoded, exc.end) - codecs.register_error('surrogateescape', surrogateescape_handler) - -def exception_encode(ex, codec): - if str == bytes: - reduced = ex.__reduce__() - ex = reduced[0](*tuple(map(lambda arg: codec.decode(arg)[0] if isinstance(arg, bytes) else arg, reduced[1]))) - return ex - -def sql_commands(read_line): - delims = ['"', "'", ';', '--'] - counter = 0 - in_string = None - j = i = 0 - prev_line = None - line = None - concat = [] - while True: - if line is None: - while True: # process preprocessor directives - counter += 1 - not_in_the_middle_of_any_input = not in_string and i == j and all(map(lambda chunk_: len(chunk_) == 0, concat)) - line = read_line(counter - 1, not_in_the_middle_of_any_input, prev_line) - empty_string = line[:0] if line is not None else line - prev_line = line - if not line: - break - if not_in_the_middle_of_any_input and line.startswith("."): - yield line - line = None - else: - break - if not line: - break - j = i = 0 - if j < len(line): - (j, delim) = min(map(lambda pair: pair if pair[0] >= 0 else (len(line), pair[1]), map(lambda d: (line.find(d, j), d), in_string or delims if in_string != '--' else "\n"))) - if i < j: concat.append(line[i:j]); i = j - if not in_string: - if j < len(line): - j += len(delim) - if delim == ';': - i = j - concat.append(line[j : j + len(delim)]) # ensure delimeter is the same type as the string (it may not be due to implicit conversion) - # Eat up any further spaces until a newline - while j < len(line): - delim = line[j:j+1] - if not delim.isspace(): break - j += 1 - if delim == "\n": break - if i < j: concat.append(line[i:j]); i = j - yield empty_string.join(concat) - del concat[:] - else: - in_string = delim - else: - if j < len(line): - ch = line[j:j+1] - assert ch == in_string or in_string == '--' - j += 1 - i = j - concat.append(ch) - in_string = None - else: - if i < j: concat.append(line[i:j]); i = j - line = None - -class WindowsConsoleIOMixin(object): - # Ctrl+C handling with ReadFile() is messed up on Windows starting on Windows 8... here's some background reading: - # https://stackoverflow.com/a/43260436 - # https://github.com/microsoft/terminal/issues/334 - # We use ReadConsole when we can, so it doesn't affect us, but it's good info to know regardless. - def __init__(self, fd): - assert isatty(fd), "file descriptor must refer to a console (note that on Windows, NUL satisfies isatty(), but is not a console)" - self.fd = fd - self.handle = msvcrt.get_osfhandle(fd) - def fileno(self): return self.fd - def isatty(self): return isatty(self.fd) - def seekable(self): return False - def readable(self): return GetNumberOfConsoleInputEvents(self.handle, ctypes.byref(DWORD(0))) != 0 - def writable(self): n = DWORD(0); return WriteConsoleW(self.handle, ctypes.c_void_p(), n, ctypes.byref(n), ctypes.c_void_p()) != 0 - def readwcharsinto(self, buf, n): - nr = DWORD(n) - old_error = ctypes.get_last_error() - ctypes.set_last_error(0) - success = ReadConsoleW(self.handle, buf, nr, ctypes.byref(nr), ctypes.c_void_p()) - error = ctypes.get_last_error() - ctypes.set_last_error(old_error) - if not success: raise ctypes.WinError(error) - ERROR_OPERATION_ABORTED = 995 - if nr.value == 0 and error == ERROR_OPERATION_ABORTED: - # Apparently this can trigger pending KeyboardInterrupts? - time.sleep(1.0 / (1 << 64)) - raise KeyboardInterrupt() # If Python doesn't raise it, we can - return nr.value - def writewchars(self, buf, n): - nw = DWORD(n) - if not WriteConsoleW(self.handle, buf, nw, ctypes.byref(nw), ctypes.c_void_p()): - raise ctypes.WinError() - return nw.value - -class WindowsConsoleRawIO(WindowsConsoleIOMixin, io.RawIOBase): - def readinto(self, b): - wordsize = ctypes.sizeof(ctypes.c_wchar) - return self.readwcharsinto(getbuffer(b, True), len(b) // wordsize) * wordsize - def write(self, b): - wordsize = ctypes.sizeof(ctypes.c_wchar) - return self.writewchars(getbuffer(b, False), len(b) // wordsize) * wordsize - -class WindowsConsoleTextIO(WindowsConsoleIOMixin, io.TextIOBase): - buf = None - buffered = unicode() - translate = True - def getbuf(self, ncodeunits): - buf = self.buf - if buf is None or len(buf) < ncodeunits: - self.buf = buf = ctypes.create_unicode_buffer(ncodeunits) - return buf - @staticmethod # Don't let classes override this... they can override the caller instead - def do_read(self, nchars, translate_newlines): - prenewline = os.linesep[:-1] - newline = os.linesep[-1:] - empty = os.linesep[:0] - if nchars is None or nchars < -1: nchars = -1 - ncodeunits = nchars if nchars >= 0 else io.DEFAULT_BUFFER_SIZE # Unit mismatch, but doesn't matter; we'll loop - buf = None - istart = 0 - while True: - iend = self.buffered.find(newline, istart, min(istart + nchars, len(self.buffered)) if nchars >= 0 else None) if newline is not None else nchars - if iend >= 0: iend += len(newline) if newline is not None else 0 - if 0 <= iend <= len(self.buffered): - break - if buf is None: buf = self.getbuf(ncodeunits) - istart = len(self.buffered) - chunk = buf[:self.readwcharsinto(buf, ncodeunits)] - if translate_newlines: chunk = chunk.replace(prenewline, empty) - if chunk.startswith('\x1A'): # EOF on Windows (Ctrl+Z) at the beginning of a line results in the entire rest of the buffer being discarded - iend = istart - break - # Python 2 and Python 3 behaviors differ on Windows... Python 2's sys.stdin.readline() just deletes the next character if it sees EOF in the middle of a string! I won't emulate that here. - self.buffered += chunk # We're relying on Python's concatenation optimization here... we don't do it ourselves, since we want self.buffered to be valid every iteration in case there is an exception raised - result = self.buffered[:iend] - self.buffered = self.buffered[iend:] - return result - def read(self, nchars=-1): return WindowsConsoleTextIO.do_read(self, nchars, None, self.translate) - def readline(self, nchars=-1): return WindowsConsoleTextIO.do_read(self, nchars, self.translate) - def write(self, text): buf = ctypes.create_unicode_buffer(text); return self.writewchars(buf, max(len(buf) - 1, 0)) - -def wrap_windows_console_io(stream, is_output): - fd = None - if stream is not None and sys.version_info[0] < 3 and msvcrt and (is_output or pythonapi) and isatty(stream): - try: fd = stream.fileno() - except io.UnsupportedOperation: pass - result = stream - if fd is not None: - f = GetConsoleOutputCP if is_output else GetConsoleCP - if not f or f() != CP_UTF8: - try: - if True or is_output: - result = WindowsConsoleTextIO(fd) - else: - result = io.TextIOWrapper((io.BufferedWriter if is_output else io.BufferedReader)(WindowsConsoleRawIO(fd)), 'utf-16-le', 'strict', line_buffering=True) - except IOError: pass - return result - -class NonOwningTextIOWrapper(io.TextIOWrapper): - def __init__(self, base_textiowrapper, **kwargs): - assert isinstance(base_textiowrapper, io.TextIOWrapper) - self.base = base_textiowrapper # must keep a reference to this alive so it doesn't get closed - super(NonOwningTextIOWrapper, self).__init__(base_textiowrapper.buffer, **kwargs) - def close(self): - super(NonOwningTextIOWrapper, self).flush() - -def wrap_unicode_stdio(stream, is_writer, encoding): # The underlying stream must NOT be used directly until the stream returned by this function is disposed of - if isinstance(stream, io.TextIOWrapper): - stream.flush() # Make sure nothing is left in the buffer before we re-wrap it - none = object() - kwargs = {} - for key in ['encoding', 'errors', 'newline', 'line_buffering', 'write_through']: - value = getattr(stream, 'newlines' if key == 'newline' else key, none) - if value is not none: - kwargs[key] = value - kwargs['encoding'] = encoding - result = NonOwningTextIOWrapper(stream, **kwargs) - elif 'PYTHONIOENCODING' not in os.environ and str == bytes and stream in (sys.stdin, sys.stdout, sys.stderr): - result = (codecs.getwriter if is_writer else codecs.getreader)(encoding)(stream) - else: - result = stream - return result - -class StringEscapeParser(object): - def __init__(self): - import re - self.pattern = re.compile("\"((?:[^\"\\n]+|\\\\.)*)(?:\"|$)|\'([^\'\\n]*)(?:\'|$)|(\\S+)") - self.escape_pattern = re.compile("\\\\(.)", re.DOTALL) - @staticmethod - def escape_replacement(m): - text = m.group(1) - if text == "\\": text = "\\" - elif text == "/": text = "\n" - elif text == "n": text = "\n" - elif text == "r": text = "\r" - elif text == "t": text = "\t" - elif text == "v": text = "\v" - elif text == "f": text = "\f" - elif text == "a": text = "\a" - elif text == "b": text = "\b" - return text - def __call__(self, s): - escape_pattern = self.escape_pattern - escape_replacement = self.escape_replacement - result = [] - for match in self.pattern.finditer(s): - [m1, m2, m3] = match.groups() - if m1 is not None: result.append(escape_pattern.sub(escape_replacement, m1)) - if m2 is not None: result.append(m2) - if m3 is not None: result.append(escape_pattern.sub(escape_replacement, m3)) - return result - -class Database(object): - def __init__(self, name, *args, **kwargs): - self.connection = sqlite3.connect(name, *args, **kwargs) - self.cursor = self.connection.cursor() - self.name = name # assign name only AFTER cursor is created - -def isatty(file_or_fd): - result = True - method = getattr(file_or_fd, 'isatty', None) if not isinstance(file_or_fd, int) else None # this check is just an optimization - if method is not None: - try: tty = method() - except io.UnsupportedOperation: tty = None - result = result and tty is not None and tty - method = getattr(file_or_fd, 'fileno', None) if not isinstance(file_or_fd, int) else None # this check is just an optimization - if method is not None: - try: fd = method() - except io.UnsupportedOperation: fd = None - result = result and fd is not None and os.isatty(fd) and (not msvcrt or GetConsoleMode(msvcrt.get_osfhandle(fd), ctypes.byref(DWORD(0))) != 0) - return result - -def can_call_input_for_stdio(stream): - return stream == sys.stdin and sys.version_info[0] >= 3 - -class StdIOProxy(object): - # Maybe useful later: codecs.StreamRecoder(bytesIO, codec.decode, codec.encode, codec.streamwriter, codec.streamreader, errors='surrogateescape') - def __init__(self, stdin, stdout, stderr, codec, allow_set_code_page): - self.codec = codec - streams = (stdin, stdout, stderr) - for stream in streams: - assert isinstance(stream, io.IOBase) or sys.version_info[0] < 3 and isinstance(stream, file) or hasattr(stream, 'mode'), "unable to determine stream type" - assert not isinstance(stream, io.RawIOBase), "RAW I/O APIs are different and not supported" - self.streaminfos = tuple(map(lambda stream: - ( - stream, - isinstance(stream, io.BufferedIOBase) or isinstance(stream, io.RawIOBase) or not isinstance(stream, io.TextIOBase) and 'b' in stream.mode, - isinstance(stream, io.TextIOBase) or not (isinstance(stream, io.BufferedIOBase) or isinstance(stream, io.RawIOBase)) and 'b' not in stream.mode, - allow_set_code_page - ), - streams)) - @property - def stdin(self): return self.streaminfos[0][0] - @property - def stdout(self): return self.streaminfos[1][0] - @property - def stderr(self): return self.streaminfos[2][0] - def _coerce(self, streaminfo, codec, arg): - stream = streaminfo[0] - can_binary = streaminfo[1] - can_text = streaminfo[2] - if not isinstance(arg, bytes) and not isinstance(arg, buffer) and not isinstance(arg, unicode): - arg = unicode(arg) - if isinstance(arg, bytes) or isinstance(arg, buffer): - if not can_binary: - arg = codec.decode(arg, 'surrogateescape')[0] - elif isinstance(arg, unicode): - if not can_text: - arg = codec.encode(unicode(arg), 'strict')[0] - return arg - @staticmethod - def _do_readline(stream, allow_set_code_page, *args): - new_code_page = CP_UTF8 - old_code_page = GetConsoleCP() if msvcrt and GetConsoleCP and isatty(stream) else None - if old_code_page == new_code_page: old_code_page = None # Don't change code page if it's already correct... - if old_code_page is not None: - if not SetConsoleCP(new_code_page): - old_code_page = None - try: - result = stream.readline(*args) - finally: - if old_code_page is not None: - SetConsoleCP(old_code_page) - return result - @staticmethod - def _do_write(stream, allow_set_code_page, *args): - new_code_page = CP_UTF8 - old_code_page = GetConsoleOutputCP() if msvcrt and GetConsoleOutputCP and isatty(stream) else None - if old_code_page == new_code_page: old_code_page = None # Don't change code page if it's already correct... - if old_code_page is not None: - if not SetConsoleOutputCP(new_code_page): - old_code_page = None - try: - result = stream.write(*args) - finally: - if old_code_page is not None: - SetConsoleCP(old_code_page) - return result - def _readln(self, streaminfo, codec, prompt): - stream = streaminfo[0] - can_binary = streaminfo[1] - allow_set_code_page = streaminfo[3] - if can_call_input_for_stdio(stream) and not can_binary: # input() can't work with binary data - result = self._coerce(streaminfo, codec, "") - try: - result = input(*((self._coerce(streaminfo, codec, prompt),) if prompt is not None else ())) - result += self._coerce(streaminfo, codec, "\n") - except EOFError: pass - else: - self.output(*((prompt,) if prompt is not None else ())) - self.error() - result = StdIOProxy._do_readline(stream, allow_set_code_page) - return result - def _writeln(self, streaminfo, codec, *args, **kwargs): - stream = streaminfo[0] - allow_set_code_page = streaminfo[3] - flush = kwargs.pop('flush', True) - kwargs.setdefault('end', '\n') - kwargs.setdefault('sep', ' ') - end = kwargs.get('end') - sep = kwargs.get('sep') - first = True - for arg in args: - if first: first = False - elif sep is not None: - StdIOProxy._do_write(stream, allow_set_code_page, self._coerce(streaminfo, codec, sep)) - StdIOProxy._do_write(stream, allow_set_code_page, self._coerce(streaminfo, codec, arg)) - if end is not None: - StdIOProxy._do_write(stream, allow_set_code_page, self._coerce(streaminfo, codec, end)) - if flush: stream.flush() - def inputln(self, prompt=None): return self._readln(self.streaminfos[0], self.codec, prompt) - def output(self, *args, **kwargs): kwargs.setdefault('end', None); return self._writeln(self.streaminfos[1], self.codec, *args, **kwargs) - def outputln(self, *args, **kwargs): return self._writeln(self.streaminfos[1], self.codec, *args, **kwargs) - def error(self, *args, **kwargs): kwargs.setdefault('end', None); return self._writeln(self.streaminfos[2], self.codec, *args, **kwargs) - def errorln(self, *args, **kwargs): return self._writeln(self.streaminfos[2], self.codec, *args, **kwargs) - -class bytes_comparable_with_unicode(bytes): # For Python 2/3 compatibility, to allow implicit conversion between strings and bytes when it is safe. (Used for strings like literals which we know be safe.) - codec = codecs.lookup('ascii') # MUST be a safe encoding - @classmethod - def coerce(cls, other, for_output=False): - return cls.codec.encode(other)[0] if not isinstance(other, bytes) else bytes_comparable_with_unicode(other) if for_output else other - @classmethod - def translate_if_bytes(cls, value): - if value is not None and isinstance(value, bytes): value = cls(value) - return value - def __hash__(self): return super(bytes_comparable_with_unicode, self).__hash__() # To avoid warning - def __eq__(self, other): return super(bytes_comparable_with_unicode, self).__eq__(self.coerce(other)) - def __ne__(self, other): return super(bytes_comparable_with_unicode, self).__ne__(self.coerce(other)) - def __lt__(self, other): return super(bytes_comparable_with_unicode, self).__lt__(self.coerce(other)) - def __gt__(self, other): return super(bytes_comparable_with_unicode, self).__gt__(self.coerce(other)) - def __le__(self, other): return super(bytes_comparable_with_unicode, self).__le__(self.coerce(other)) - def __ge__(self, other): return super(bytes_comparable_with_unicode, self).__ge__(self.coerce(other)) - def __getitem__(self, index): return self.coerce(super(bytes_comparable_with_unicode, self).__getitem__(index), True) - def __add__(self, other): return self.coerce(super(bytes_comparable_with_unicode, self).__add__(self.coerce(other)), True) - def __iadd__(self, other): return self.coerce(super(bytes_comparable_with_unicode, self).__iadd__(self.coerce(other)), True) - def __radd__(self, other): return self.coerce(self.coerce(other).__add__(self), True) - def find(self, other, *args): return super(bytes_comparable_with_unicode, self).find(self.coerce(other), *args) - def join(self, others): return self.coerce(super(bytes_comparable_with_unicode, self).join(map(self.coerce, others)), True) - def startswith(self, other): return super(bytes_comparable_with_unicode, self).startswith(self.coerce(other)) - def __str__(self): return self.codec.decode(self)[0] - if str == bytes: - __unicode__ = __str__ - def __str__(self): raise NotImplementedError() - -def wrap_bytes_comparable_with_unicode_readline(readline): - def callback(*args): - line = readline(*args) - line = bytes_comparable_with_unicode.translate_if_bytes(line) - return line - return callback - -def main(program, *args, **kwargs): # **kwargs = dict(stdin=file, stdout=file, stderr=file); useful for callers who import this module - import argparse # slow import (compiles regexes etc.), so don't import it until needed - argparser = argparse.ArgumentParser( - prog=os.path.basename(program), - usage=None, - description=None, - epilog=None, - parents=[], - formatter_class=argparse.RawTextHelpFormatter) - argparser.add_argument('-version', '--version', action='store_true', help="show SQLite version") - argparser.add_argument('-batch', '--batch', action='store_true', help="force batch I/O") - argparser.add_argument('-init', '--init', metavar="FILE", help="read/process named file") - argparser.add_argument('filename', nargs='?', metavar="FILENAME", help="is the name of an SQLite database.\nA new database is created if the file does not previously exist.") - argparser.add_argument('sql', nargs='*', metavar="SQL", help="SQL commnds to execute after opening database") - argparser.add_argument('--readline', action='store', metavar="(true|false)", default="true", choices=("true", "false"), help="whether to import readline if available (default: %(default)s)") - argparser.add_argument('--self-test', action='store_true', help="perform a basic self-test") - argparser.add_argument('--cross-test', action='store_true', help="perform a basic test against the official executable") - argparser.add_argument('--unicode-stdio', action='store', metavar="(true|false)", default="true", choices=("true", "false"), help="whether to enable Unicode wrapper for standard I/O (default: %(default)s)") - argparser.add_argument('--console', action='store', metavar="(true|false)", default="true", choices=("true", "false"), help="whether to auto-detect and use console window APIs (default: %(default)s)") - argparser.add_argument('--encoding', default=ENCODING, help="the default encoding to use (default: %(default)s)") - (stdin, stdout, stderr) = (kwargs.pop('stdin', sys.stdin), kwargs.pop('stdout', sys.stdout), kwargs.pop('stderr', sys.stderr)) - parsed_args = argparser.parse_args(args) - codec = codecs.lookup(parsed_args.encoding or argparser.get_default('encoding')) - if parsed_args.self_test: self_test(codec) - if parsed_args.cross_test: cross_test("sqlite3", codec) - parse_escaped_strings = StringEscapeParser() - if parsed_args.unicode_stdio == "true": - stdin = wrap_unicode_stdio(stdin, False, codec.name) - stdout = wrap_unicode_stdio(stdout, True, codec.name) - stderr = wrap_unicode_stdio(stderr, True, codec.name) - if parsed_args.console == "true": - stdin = wrap_windows_console_io(stdin, False) - stdout = wrap_windows_console_io(stdout, True) - stderr = wrap_windows_console_io(stderr, True) - allow_set_code_page = sys.version_info[0] < 3 and False # This is only necessary on Python 2 if we use the default I/O functions instead of bypassing to ReadConsole()/WriteConsole() - stdio = StdIOProxy(stdin, stdout, stderr, codec, allow_set_code_page) - db = None - no_args = len(args) == 0 - init_sql = parsed_args.sql - is_nonpipe_input = stdin.isatty() # NOT the same thing as TTY! (NUL and /dev/null are the difference) - init_show_prompt = not parsed_args.batch and is_nonpipe_input - if not parsed_args.batch and isatty(stdin) and (parsed_args.readline == "true" or __name__ == '__main__') and parsed_args.readline != "false": - try: - with warnings.catch_warnings(): - warnings.filterwarnings('ignore', category=DeprecationWarning) - import readline - except ImportError: pass - if parsed_args and parsed_args.version: - stdio.outputln(sqlite3.sqlite_version); - else: - filename = parsed_args.filename - if filename is None: filename = ":memory:" - db = Database(filename, isolation_level=None) - def exec_script(db, filename, ignore_io_errors): - try: - with io.open(filename, 'r', encoding=codec.name) as f: # Assume .sql files are text -- any binary data inside them should be X'' encoded, not embedded directly - for command in sql_commands(wrap_bytes_comparable_with_unicode_readline(lambda *args: (lambda s: (s) or None)(f.readline()))): - result = exec_command(db, command, False and ignore_io_errors) - if result is not None: - return result - except IOError as ex: - stdio.errorln(ex) - if not ignore_io_errors: return ex.errno - def raise_invalid_command_error(command): - if isinstance(command, bytes): command = codec.decode(command)[0] - if command.startswith("."): command = command[1:] - raise RuntimeError("Error: unknown command or invalid arguments: \"%s\". Enter \".help\" for help" % (command.rstrip().replace("\\", "\\\\").replace("\"", "\\\""),)) - def exec_command(db, command, ignore_io_errors): - results = None - query = None - query_parameters = {} - try: - if command.startswith("."): - args = list(parse_escaped_strings(command)) - if args[0] in (".quit", ".exit"): - return 0 - elif args[0] == ".help": - stdio.error(""" -.cd DIRECTORY Change the working directory to DIRECTORY -.dump Dump the database in an SQL text format -.exit Exit this program -.help Show this message -.open FILE Close existing database and reopen FILE -.print STRING... Print literal STRING -.quit Exit this program -.read FILENAME Execute SQL in FILENAME -.schema ?PATTERN? Show the CREATE statements matching PATTERN -.show Show the current values for various settings -.tables ?TABLE? List names of tables -""".lstrip()) - elif args[0] == ".cd": - if len(args) != 2: raise_invalid_command_error(command) - os.chdir(args[1]) - elif args[0] == ".dump": - if len(args) != 1: raise_invalid_command_error(command) - foreign_keys = db.cursor.execute("PRAGMA foreign_keys;").fetchone()[0] - if foreign_keys in (0, "0", "off", "OFF"): - stdio.outputln("PRAGMA foreign_keys=OFF;", flush=False) - for line in db.connection.iterdump(): - stdio.outputln(line, flush=False) - stdio.output() - elif args[0] == ".open": - if len(args) <= 1: raise_invalid_command_error(command) - filename = args[-1] - for option in args[+1:-1]: - raise ValueError("option %s not supported" % (repr(option),)) - try: db.__init__(filename) - except sqlite3.OperationalError as ex: - ex.args = ex.args[:0] + ("Error: unable to open database \"%s\": %s" % (filename, ex.args[0]),) + ex.args[1:] - raise - elif args[0] == ".print": - stdio.outputln(*args[1:]) - elif args[0] == ".read": - if len(args) != 2: raise_invalid_command_error(command) - exec_script(db, args[1], ignore_io_errors) - elif args[0] == ".schema": - if len(args) > 2: raise_invalid_command_error(command) - pattern = args[1] if len(args) > 1 else None - query_parameters['type'] = 'table' - if pattern is not None: - query_parameters['pattern'] = pattern - query = "SELECT sql || ';' FROM sqlite_master WHERE type = :type" + (" AND name LIKE :pattern" if pattern is not None else "") + ";" - elif args[0] == ".show": - if len(args) > 2: raise_invalid_command_error(command) - stdio.errorln(" filename:", db.name) - elif args[0] == ".tables": - if len(args) > 2: raise_invalid_command_error(command) - pattern = args[1] if len(args) > 1 else None - query_parameters['type'] = 'table' - if pattern is not None: - query_parameters['pattern'] = pattern - query = "SELECT name FROM sqlite_master WHERE type = :type" + (" AND name LIKE :pattern" if pattern is not None else "") + ";" - else: - raise_invalid_command_error(args[0]) - else: - query = command - if query is not None: - results = db.cursor.execute(query if isinstance(query, unicode) else codec.decode(query, 'surrogatereplace')[0], query_parameters) - except (RuntimeError, OSError, FileNotFoundError, sqlite3.OperationalError) as ex: - stdio.errorln(exception_encode(ex, codec)) - if results is not None: - for row in results: - stdio.outputln(*tuple(map(lambda item: item if item is not None else "", row)), sep="|", flush=False) - stdio.output() - if db: - if parsed_args and parsed_args.init: - if is_nonpipe_input: stdio.errorln("-- Loading resources from", parsed_args.init) - exec_script(db, parsed_args.init, False) - def read_stdin(index, not_in_the_middle_of_any_input, prev_line): - show_prompt = init_show_prompt - to_write = [] - if index < len(init_sql): - line = init_sql[index] - if not line.startswith(".") and not line.rstrip().endswith(";"): - line += ";" - elif index == len(init_sql) and len(init_sql) > 0: - line = None - else: - if show_prompt: - if not_in_the_middle_of_any_input: - show_prompt = False - if index == 0: - to_write.append("SQLite version %s (adapter version %s)\nEnter \".help\" for usage hints.\n" % (sqlite3.sqlite_version, sqlite3.version)) - if no_args: - to_write.append("Connected to a transient in-memory database.\nUse \".open FILENAME\" to reopen on a persistent database.\n") - if index > 0 and not prev_line: - to_write.append("\n") - to_write.append("%7s " % ("sqlite%s>" % ("",) if not_in_the_middle_of_any_input else "...>",)) - try: - line = stdio.inputln("".join(to_write)) - except KeyboardInterrupt: - line = "" - raise # just kidding, don't handle it for now... - return line - for command in sql_commands(wrap_bytes_comparable_with_unicode_readline(read_stdin)): - result = exec_command(db, command, True) - if result is not None: - return result - if init_show_prompt and len(init_sql) == 0: - stdio.outputln() - -def call_program(cmdline, input_text): - import subprocess - return subprocess.Popen(cmdline, bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=False).communicate(input_text) - -def test_query(): - hexcodec = codecs.lookup('hex_codec') - ascii = 'ascii' - data1 = b"\xD8\xA2" - data2 = b"\x01\x02\xFF\x01\xFF\xFE\xFD" - values = [data1, data2] - query_bytes = b'SELECT %s;' % (b", ".join(map(lambda b: b"X'%s'" % (hexcodec.encode(b)[0].upper(),), values)),) - expected_bytes = b"%s\n" % (b"|".join(values),) - return query_bytes, expected_bytes - -def cross_test(sqlite_cmdline, codec): - (query_bytes, expected_bytes) = test_query() - (official_output, official_error) = call_program(sqlite_cmdline, query_bytes) - # We can't use os.linesep here since binaries may belong to different platforms (Win32/MinGW vs. MSYS/Cygwin vs. WSL...) - official_output = official_output.replace(b"\r\n", b"\n") - official_error = official_error.replace(b"\r\n", b"\n") - if official_output != expected_bytes: - raise sqlite3.ProgrammingError("expected bytes are wrong: official %s != expected %s" % (repr(official_output), repr(expected_bytes))) - if official_error: - raise sqlite3.ProgrammingError("did not expect errors from official binary") - -def self_test(codec): - (query_bytes, expected_bytes) = test_query() - if not (lambda stdin, stdout, stderr: not main(sys.argv[0], stdin=stdin, stdout=stdout, stderr=stderr) and stdout.getvalue() == expected_bytes)(io.BytesIO(query_bytes), io.BytesIO(), io.BytesIO()): - raise sqlite3.ProgrammingError("byte I/O is broken") - if not (lambda stdin, stdout, stderr: not main(sys.argv[0], stdin=stdin, stdout=stdout, stderr=stderr) and stdout.getvalue() == codec.decode(expected_bytes, 'surrogateescape'))(io.StringIO(query_bytes.decode(ascii)), io.StringIO(), io.StringIO()): - raise sqlite3.ProgrammingError("string I/O is broken") - -if __name__ == '__main__': - import sys - exit_code = main(*sys.argv) - if exit_code not in (None, 0): raise SystemExit(exit_code) diff --git a/lab/sol-lab12/tests/big.py b/lab/sol-lab12/tests/big.py deleted file mode 100644 index c0bff9900b..0000000000 --- a/lab/sol-lab12/tests/big.py +++ /dev/null @@ -1,23 +0,0 @@ -test = { - 'name': 'big', - 'points': 1, - 'suites': [ - { - 'type': 'sqlite', - 'setup': """ - sqlite> .read lab12.sql - """, - 'cases': [ - { - 'locked': False, - 'code': r""" - sqlite> SELECT * FROM big; - 61A - 61B - 61C - """, - }, - ], - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab12/tests/meals-part1.py b/lab/sol-lab12/tests/meals-part1.py deleted file mode 100644 index fb0b4c2715..0000000000 --- a/lab/sol-lab12/tests/meals-part1.py +++ /dev/null @@ -1,21 +0,0 @@ -test = { - 'name': 'meals-part1', - 'points': 1, - 'suites': [ - { - 'type': 'sqlite', - 'setup': """ - sqlite> .read lab12.sql - """, - 'cases': [ - { - 'locked': False, - 'code': r""" - sqlite> SELECT * FROM number_of_options; - 4 - """, - }, - ], - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab12/tests/meals-part2.py b/lab/sol-lab12/tests/meals-part2.py deleted file mode 100644 index 0e55cc9172..0000000000 --- a/lab/sol-lab12/tests/meals-part2.py +++ /dev/null @@ -1,21 +0,0 @@ -test = { - 'name': 'meals-part2', - 'points': 1, - 'suites': [ - { - 'type': 'sqlite', - 'setup': """ - sqlite> .read lab12.sql - """, - 'cases': [ - { - 'locked': False, - 'code': r""" - sqlite> SELECT * FROM calories; - 22 - """, - }, - ], - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab12/tests/meals-part3.py b/lab/sol-lab12/tests/meals-part3.py deleted file mode 100644 index 91f274d448..0000000000 --- a/lab/sol-lab12/tests/meals-part3.py +++ /dev/null @@ -1,22 +0,0 @@ -test = { - 'name': 'meals-part3', - 'points': 1, - 'suites': [ - { - 'type': 'sqlite', - 'setup': """ - sqlite> .read lab12.sql - """, - 'cases': [ - { - 'locked': False, - 'code': r""" - sqlite> SELECT * FROM healthiest_meats; - tofurky|1400 - turkey|1900 - """, - }, - ], - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab12/tests/no_repeats.py b/lab/sol-lab12/tests/no_repeats.py deleted file mode 100644 index 477c7bc58b..0000000000 --- a/lab/sol-lab12/tests/no_repeats.py +++ /dev/null @@ -1,84 +0,0 @@ -test = { - 'name': 'no-repeats', - 'points': 1, - 'suites': [ - { - 'scored': True, - 'setup': """ - scm> (load-all ".") - """, - 'type': 'scheme', - 'cases': [ - { - 'code': """ - scm> (no-repeats '(5 4 3 2 1)) - (5 4 3 2 1) - """, - 'hidden': False, - "locked": False - }, - { - 'code': """ - scm> (no-repeats '(5 4 3 2 1 1)) - (5 4 3 2 1) - """, - 'hidden': False, - "locked": False - }, - { - 'code': """ - scm> (no-repeats '(5 5 4 3 2 1)) - (5 4 3 2 1) - """, - 'hidden': False, - "locked": False - }, - { - 'code': """ - scm> (no-repeats '(12)) - (12) - """, - 'hidden': False, - "locked": False - }, - { - 'code': """ - scm> (no-repeats '(1 1 1 1 1 1)) - (1) - """, - 'hidden': False, - "locked": False - }, - ] - }, - { - 'scored': True, - 'setup': """ - scm> (load-all ".") - """, - 'type': 'scheme', - 'cases': [ - { - 'code': """ - scm> (no-repeats (list 5 4 2)) - (5 4 2) - """, - 'hidden': False, - "locked": False - }, - { - 'code': """ - scm> (no-repeats (list 5 4 5 4 2 2)) - (5 4 2) - scm> (no-repeats (list 5 5 5 5 5)) - (5) - scm> (no-repeats ()) - () - """, - 'hidden': False, - "locked": False - }, - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab12/tests/pairs.py b/lab/sol-lab12/tests/pairs.py deleted file mode 100644 index c68fd04009..0000000000 --- a/lab/sol-lab12/tests/pairs.py +++ /dev/null @@ -1,28 +0,0 @@ -test = { - 'name': 'pairs', - 'points': 1, - 'suites': [ - { - 'type': 'sqlite', - 'setup': """ - sqlite> .read lab12.sql - """, - 'cases': [ - { - 'locked': False, - 'code': r""" - sqlite> SELECT * FROM pairs; - RSF and Wheeler together have 1600 seats - Pimentel and RSF together have 1400 seats - Li Ka Shing and RSF together have 1200 seats - Pimentel and Wheeler together have 1200 seats - RSF and Stanley together have 1200 seats - Li Ka Shing and Wheeler together have 1000 seats - Morgan and RSF together have 1000 seats - Stanley and Wheeler together have 1000 seats - """, - }, - ], - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab12/tests/remaining.py b/lab/sol-lab12/tests/remaining.py deleted file mode 100644 index 503605d47e..0000000000 --- a/lab/sol-lab12/tests/remaining.py +++ /dev/null @@ -1,25 +0,0 @@ -test = { - 'name': 'remaining', - 'points': 1, - 'suites': [ - { - 'type': 'sqlite', - 'setup': """ - sqlite> .read lab12.sql - """, - 'cases': [ - { - 'locked': False, - 'code': r""" - sqlite> SELECT course, remaining FROM remaining; - 10|0 - 61A|1800 - 61B|800 - 61C|540 - 70|0 - """, - }, - ], - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab12/tests/reverse-simple.py b/lab/sol-lab12/tests/reverse-simple.py deleted file mode 100644 index 4470e2269c..0000000000 --- a/lab/sol-lab12/tests/reverse-simple.py +++ /dev/null @@ -1,29 +0,0 @@ -test = { - 'name': 'reverse-simple', - 'points': 1, - 'suites': [ - { - 'type': 'scheme', - 'cases': [ - { - 'code': r""" - scm> (reverse '()) - () - scm> (reverse '(1)) - (1) - scm> (reverse '(1 2 3)) - (3 2 1) - scm> (reverse '(1 2 3 4 5)) - (5 4 3 2 1) - scm> (reverse '(1 2 3 4 5 1 3 7)) - (7 3 1 5 4 3 2 1) - """, - 'locked' : False, - }, - ], - 'setup': r""" - scm> (load-all ".") - """, - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab12/tests/sharing.py b/lab/sol-lab12/tests/sharing.py deleted file mode 100644 index b69e950fdb..0000000000 --- a/lab/sol-lab12/tests/sharing.py +++ /dev/null @@ -1,24 +0,0 @@ -test = { - 'name': 'sharing', - 'points': 1, - 'suites': [ - { - 'type': 'sqlite', - 'setup': """ - sqlite> .read lab12.sql - """, - 'cases': [ - { - 'locked': False, - 'code': r""" - sqlite> SELECT * FROM sharing; - 61A|3 - 61B|2 - 61C|2 - 70|1 - """, - }, - ], - }, - ] -} \ No newline at end of file diff --git a/lab/sol-lab12/tests/without_duplicates.py b/lab/sol-lab12/tests/without_duplicates.py deleted file mode 100644 index fa8920c9d9..0000000000 --- a/lab/sol-lab12/tests/without_duplicates.py +++ /dev/null @@ -1,49 +0,0 @@ -test = { - 'name': 'without-duplicates', - 'points': 1, - 'suites': [ - { - 'scored': True, - 'setup': """ - scm> (load-all ".") - """, - 'type': 'scheme', - 'cases': [ - { - 'code': """ - scm> (without-duplicates (list 5 4 2)) - (5 4 2) - """, - 'hidden': False - }, - { - 'code': """ - scm> (without-duplicates (list 5 4 5 4 2 2)) - (5 4 2) - scm> (without-duplicates (list 5 5 5 5 5)) - (5) - scm> (without-duplicates ()) - () - """, - 'hidden': False - }, - { - 'code': """ - scm> (without-duplicates '(5 4 3 2 1)) - (5 4 3 2 1) - scm> (without-duplicates '(5 4 3 2 1 1)) - (5 4 3 2 1) - scm> (without-duplicates '(5 5 4 3 2 1)) - (5 4 3 2 1) - scm> (without-duplicates '(12)) - (12) - scm> (without-duplicates '(1 1 1 1 1 1)) - (1) - """, - 'hidden': False, - 'locked': False - } - ] - } - ] -} \ No newline at end of file diff --git a/lab/sol-lab12/tree.py b/lab/sol-lab12/tree.py deleted file mode 100644 index c4e336dcf0..0000000000 --- a/lab/sol-lab12/tree.py +++ /dev/null @@ -1,33 +0,0 @@ -class Tree: - """ - >>> t = Tree(3, [Tree(2, [Tree(5)]), Tree(4)]) - >>> t.label - 3 - >>> t.branches[0].label - 2 - >>> t.branches[1].is_leaf() - True - """ - def __init__(self, label, branches=[]): - for b in branches: - assert isinstance(b, Tree) - self.label = label - self.branches = list(branches) - - def is_leaf(self): - return not self.branches - - def __repr__(self): - if self.branches: - branch_str = ', ' + repr(self.branches) - else: - branch_str = '' - return 'Tree({0}{1})'.format(self.label, branch_str) - - def __str__(self): - def print_tree(t, indent=0): - tree_str = ' ' * indent + str(t.label) + "\n" - for b in t.branches: - tree_str += print_tree(b, indent + 1) - return tree_str - return print_tree(self).rstrip() \ No newline at end of file