diff --git a/hw/hw01/hw01.zip b/hw/hw01/hw01.zip index 3ece6284b..65b50b527 100644 Binary files a/hw/hw01/hw01.zip and b/hw/hw01/hw01.zip differ diff --git a/hw/hw01/ok b/hw/hw01/ok index 88874ff1a..b84a800fe 100644 Binary files a/hw/hw01/ok and b/hw/hw01/ok differ diff --git a/hw/hw02/hw02.zip b/hw/hw02/hw02.zip index ba87b7630..c07224473 100644 Binary files a/hw/hw02/hw02.zip and b/hw/hw02/hw02.zip differ diff --git a/hw/hw02/ok b/hw/hw02/ok index 88874ff1a..b84a800fe 100644 Binary files a/hw/hw02/ok and b/hw/hw02/ok differ diff --git a/hw/hw03/hw03.zip b/hw/hw03/hw03.zip index 22245eb96..6401c69b4 100644 Binary files a/hw/hw03/hw03.zip and b/hw/hw03/hw03.zip differ diff --git a/hw/hw03/ok b/hw/hw03/ok index 88874ff1a..b84a800fe 100644 Binary files a/hw/hw03/ok and b/hw/hw03/ok differ diff --git a/hw/hw04/hw04.zip b/hw/hw04/hw04.zip index 0139437b6..270c0937c 100644 Binary files a/hw/hw04/hw04.zip and b/hw/hw04/hw04.zip differ diff --git a/hw/hw04/ok b/hw/hw04/ok index 88874ff1a..b84a800fe 100644 Binary files a/hw/hw04/ok and b/hw/hw04/ok differ diff --git a/hw/sol-hw01/hw01.zip b/hw/sol-hw01/hw01.zip index 1b75e4454..d7d92aa71 100644 Binary files a/hw/sol-hw01/hw01.zip and b/hw/sol-hw01/hw01.zip differ diff --git a/hw/sol-hw01/ok b/hw/sol-hw01/ok index 88874ff1a..b84a800fe 100644 Binary files a/hw/sol-hw01/ok and b/hw/sol-hw01/ok differ diff --git a/hw/sol-hw02/hw02.zip b/hw/sol-hw02/hw02.zip index f49648e8d..5a211d28c 100644 Binary files a/hw/sol-hw02/hw02.zip and b/hw/sol-hw02/hw02.zip differ diff --git a/hw/sol-hw02/ok b/hw/sol-hw02/ok index 88874ff1a..b84a800fe 100644 Binary files a/hw/sol-hw02/ok and b/hw/sol-hw02/ok differ diff --git a/hw/sol-hw03/construct_check.py b/hw/sol-hw03/construct_check.py new file mode 100644 index 000000000..bee096e7e --- /dev/null +++ b/hw/sol-hw03/construct_check.py @@ -0,0 +1,180 @@ +from ast import parse, NodeVisitor, Name + +# For error messages (student-facing) only +_NAMES = { + 'Add': '+', + 'And': 'and', + 'Assert': 'assert', + 'Assign': '=', + 'AnnAssign': '=', + 'AugAssign': 'op=', + 'BitAnd': '&', + 'BitOr': '|', + 'BitXor': '^', + 'Break': 'break', + 'Recursion': 'recursive call', + 'ClassDef': 'class', + 'Continue': 'continue', + 'Del': 'del', + 'Delete': 'delete', + 'Dict': '{...}', + 'DictComp': '{...}', + 'Div': '/', + 'Ellipsis': '...', + 'Eq': '==', + 'ExceptHandler': 'except', + 'ExtSlice': '[::]', + 'FloorDiv': '//', + 'For': 'for', + 'FunctionDef': 'def', + 'Filter': 'filter', + 'GeneratorExp': '(... for ...)', + 'Global': 'global', + 'Gt': '>', + 'GtE': '>=', + 'If': 'if', + 'IfExp': '...if...else...', + 'Import': 'import', + 'ImportFrom': 'from ... import ...', + 'In': 'in', + 'Index': '...[...]', + 'Invert': '~', + 'Is': 'is', + 'IsNot': 'is not ', + 'LShift': '<<', + 'Lambda': 'lambda', + 'List': '[...]', + 'ListComp': '[...for...]', + 'Lt': '<', + 'LtE': '<=', + 'Mod': '%', + 'Mult': '*', + 'NamedExpr': ':=', + 'Nonlocal': 'nonlocal', + 'Not': 'not', + 'NotEq': '!=', + 'NotIn': 'not in', + 'Or': 'or', + 'Pass': 'pass', + 'Pow': '**', + 'RShift': '>>', + 'Raise': 'raise', + 'Return': 'return', + 'Set': '{ ... } (set)', + 'SetComp': '{ ... for ... } (set)', + 'Slice': '[ : ]', + 'Starred': '', + 'Str': 'str', + 'Sub': '-', + 'Subscript': '[]', + 'Try': 'try', + 'Tuple': '(... , ... )', + 'UAdd': '+', + 'USub': '-', + 'While': 'while', + 'With': 'with', + 'Yield': 'yield', + 'YieldFrom': 'yield from', +} + +def check(source_file, checked_funcs, disallow, source=None): + """Checks that AST nodes whose type names are present in DISALLOW + (an object supporting 'in') are not present in the function(s) named + CHECKED_FUNCS in SOURCE. By default, SOURCE is the contents of the + file SOURCE_FILE. CHECKED_FUNCS is either a string (indicating a single + name) or an object of some other type that supports 'in'. CHECKED_FUNCS + may contain __main__ to indicate an entire module. Prints reports of + each prohibited node and returns True iff none are found. + See ast.__dir__() for AST type names. The special node name 'Recursion' + checks for overtly recursive calls (i.e., calls of the form NAME(...) where + NAME is an enclosing def.""" + return ExclusionChecker(disallow).check(source_file, checked_funcs, source) + +class ExclusionChecker(NodeVisitor): + """An AST visitor that checks that certain constructs are excluded from + parts of a program. ExclusionChecker(EXC) checks that AST node types + whose names are in the sequence or set EXC are not present. Its check + method visits nodes in a given function of a source file checking that the + indicated node types are not used.""" + + def __init__(self, disallow=()): + """DISALLOW is the initial default list of disallowed + node-type names.""" + self._disallow = set(disallow) + self._checking = False + self._errs = 0 + + def generic_visit(self, node): + if self._checking and type(node).__name__ in self._disallow: + self._report(node) + super().generic_visit(node) + + def visit_Module(self, node): + if "__main__" in self._checked_funcs: + self._checking = True + self._checked_name = self._source_file + super().generic_visit(node) + + def visit_Call(self, node): + if 'Recursion' in self._disallow and \ + type(node.func) is Name and \ + node.func.id in self._func_nest: + self._report(node, "should not be recursive") + self.generic_visit(node) + + def visit_FunctionDef(self, node): + self._func_nest.append(node.name) + if self._checking: + self.generic_visit(node) + elif node.name in self._checked_funcs: + self._checked_name = "Function " + node.name + checking0 = self._checking + self._checking = True + super().generic_visit(node) + self._checking = checking0 + self._func_nest.pop() + + def _report(self, node, msg=None): + node_name = _NAMES.get(type(node).__name__, type(node).__name__) + if msg is None: + msg = "should not contain '{}'".format(node_name) + print("{} {}".format(self._checked_name, msg)) + self._errs += 1 + + def errors(self): + """Returns the number of number of prohibited constructs found in + the last call to check.""" + return self._errs + + def check(self, source_file, checked_funcs, disallow=None, source=None): + """Checks that AST nodes whose type names are present in DISALLOW + (an object supporting the contains test) are not present in + the function(s) named CHECKED_FUNCS in SOURCE. By default, SOURCE + is the contents of the file SOURCE_FILE. DISALLOW defaults to the + argument given to the constructor (and resets that value if it is + present). CHECKED_FUNCS is either a string (indicating a single + name) or an object of some other type that supports 'in'. + CHECKED_FUNCS may contain __main__ to indicate an entire module. + Prints reports of each prohibited node and returns True iff none + are found. + See ast.__dir__() for AST type names. The special node name + 'Recursion' checks for overtly recursive calls (i.e., calls of the + form NAME(...) where NAME is an enclosing def.""" + + self._checking = False + self._source_file = source_file + self._func_nest = [] + if type(checked_funcs) is str: + self._checked_funcs = { checked_funcs } + else: + self._checked_funcs = set(checked_funcs) + if disallow is not None: + self._disallow = set(disallow) + if source is None: + with open(source_file, 'r', errors='ignore') as inp: + source = inp.read() + p = parse(source, source_file) + self._errs = 0 + + self.visit(p) + return self._errs == 0 \ No newline at end of file diff --git a/hw/sol-hw03/hw03.ok b/hw/sol-hw03/hw03.ok new file mode 100644 index 000000000..27e407931 --- /dev/null +++ b/hw/sol-hw03/hw03.ok @@ -0,0 +1,26 @@ +{ + "name": "Homework 3", + "endpoint": "cal/c88c/fa24/hw03", + "src": [ + "hw03.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-hw03/hw03.py b/hw/sol-hw03/hw03.py new file mode 100644 index 000000000..82057ed41 --- /dev/null +++ b/hw/sol-hw03/hw03.py @@ -0,0 +1,121 @@ +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(1) + square(2) + ... + square(4) + square(5) + 55 + >>> summation_using_accumulate(5, triple) # triple(1) + triple(2) + ... + 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-hw03/hw03.zip b/hw/sol-hw03/hw03.zip new file mode 100644 index 000000000..e6724bb37 Binary files /dev/null and b/hw/sol-hw03/hw03.zip differ diff --git a/hw/sol-hw03/index.html b/hw/sol-hw03/index.html new file mode 100644 index 000000000..34c63507f --- /dev/null +++ b/hw/sol-hw03/index.html @@ -0,0 +1,534 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Homework 3 Solutions | Data C88C Fall 2024 + + + + + + +
+ +
+
+
+

+ +Homework 3 Solutions + + + + + + +

+
+ +

Solution Files

+

You can find solutions for all questions 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

+
+ +

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(1) + square(2) + ... + square(4) + square(5)
+    55
+    >>> summation_using_accumulate(5, triple) # triple(1) + triple(2) + ... + 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.

+ +
+ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hw/sol-hw03/ok b/hw/sol-hw03/ok new file mode 100644 index 000000000..b84a800fe Binary files /dev/null and b/hw/sol-hw03/ok differ diff --git a/index.html b/index.html index 5ff4417d8..438b29150 100644 --- a/index.html +++ b/index.html @@ -146,6 +146,27 @@

Data C88C: Computational Structures in Data Science

+ + +

Announcements: Monday, September 30

+ + + +
+ + +

Announcements: Wednesday, September 25

@@ -168,7 +189,7 @@

Announcements: Wednesday, September 25

-
+

Announcements: Monday, September 23

@@ -180,7 +201,7 @@

Announcements: Monday, September 23

-
+

Announcements: Wednesday, September 18

@@ -199,7 +220,7 @@

Announcements: Wednesday, September 18

-
+

Announcements: Monday, September 16

@@ -217,7 +238,7 @@

Announcements: Monday, September 16

-
+

Announcements: Wednesday, September 11

@@ -230,7 +251,7 @@

Announcements: Wednesday, September 11

-
+

Announcements: Monday, September 9

@@ -243,7 +264,7 @@

Announcements: Monday, September 9

-
+

Announcements: Wednesday, September 4

@@ -269,7 +290,7 @@

Announcements: Wednesday, September 4

-
+

Announcements: Monday, August 26

@@ -1344,7 +1365,7 @@

Policies

}); - let build_time = new Date(1000 * 1727707594.669576); + let build_time = new Date(1000 * 1727721038.777153);