Due by 11:59pm on Saturday, 7/1

Instructions

Download hw02.zip.

Submission: When you are done, submit with python3 ok --submit. You may submit more than once before the deadline; only the final submission will be scored. Check that you have successfully submitted your code on okpy.org. See Lab 0 for more instructions on submitting assignments.

Using OK: If you have any questions about using OK, please refer to this guide.

Readings: You might find the following references useful:

Homework questions

Several doctests refer to these one-argument functions:

from operator import add, mul

def square(x):
    return x * x

def triple(x):
    return 3 * x

def identity(x):
    return x

def increment(x):
    return x + 1

Question 1: Summation

Write a summation(term, n) function that returns term(1) + ... + term(n)

def summation(n, term):
    """Return the summation of the first n terms in a sequence.

    n    -- a positive integer
    term -- a function that takes one argument

    >>> summation(3, identity) # 1 + 2 + 3
    6
    >>> summation(5, identity) # 1 + 2 + 3 + 4 + 5
    15
    >>> summation(3, square)   # 1^2 + 2^2 + 3^2
    14
    >>> summation(5, square)   # 1^2 + 2^2 + 3^2 + 4^2 + 5^2
    55
    """
    "*** YOUR CODE HERE ***"

Use OK to test your code:

python3 ok -q summation

Question 2: Product

The summation(term, n) function above adds up term(1) + ... + term(n). Write a similar product(n, term) function that returns term(1) * ... * term(n). Show how to define the factorial function in terms of product. Hint: try using the identity function for factorial.

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

    >>> 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
    """
    "*** YOUR CODE HERE ***"

# The identity function, defined using a lambda expression!
identity = lambda k: k

def factorial(n):
    """Return n factorial for n >= 0 by calling product.

    >>> factorial(4)
    24
    >>> factorial(6)
    720
    >>> from construct_check import check
    >>> check(HW_SOURCE_FILE, 'factorial', ['Recursion', 'For', 'While'])
    True
    """
    "*** YOUR CODE HERE ***"
    return _______

Use OK to test your code:

python3 ok -q product
python3 ok -q factorial

Question 3: Make Adder with a Lambda

Implement the make_adder function below using a single return statement that returns the value of a lambda expression.

def make_adder(n):
    """Return a function that takes an argument K and returns N + K.

    >>> add_three = make_adder(3)
    >>> add_three(1) + add_three(2)
    9
    >>> make_adder(1)(2)
    3
    """
    "*** YOUR CODE HERE ***"
    # return lambda ________________

Use OK to test your code:

python3 ok -q make_adder

Question 4: Accumulate

Show that both summation and product are instances of a more general function, called accumulate:

def accumulate(combiner, base, n, term):
    """Return the result of combining the first n terms in a sequence and base.
    The terms to be combined are term(1), term(2), ..., term(n).  combiner is a
    two-argument commutative 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
    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
    """
    "*** YOUR CODE HERE ***"

accumulate(combiner, base, n, term) takes the following arguments:

  • term and n: the same arguments as in summation and product
  • combiner: a two-argument function that specifies how the current term combined with the previously accumulated terms. You may assume that combiner is commutative, i.e., combiner(a, b) = combiner(b, a).
  • base: value that specifies what value to use to start the accumulation.

For example, accumulate(add, 11, 3, square) is

11 + square(1) + square(2) + square(3)

Implement accumulate and show how summation and product can both be defined as simple calls to accumulate:

def summation_using_accumulate(n, term):
    """Returns the sum of term(1) + ... + term(n). The implementation
    uses accumulate.

    >>> summation_using_accumulate(5, square)
    55
    >>> summation_using_accumulate(5, triple)
    45
    >>> from construct_check import check
    >>> check(HW_SOURCE_FILE, 'summation_using_accumulate',
    ...       ['Recursion', 'For', 'While'])
    True
    """
    "*** YOUR CODE HERE ***"
    return _______

def product_using_accumulate(n, term):
    """An implementation of product using accumulate.

    >>> product_using_accumulate(4, square)
    576
    >>> product_using_accumulate(6, triple)
    524880
    >>> from construct_check import check
    >>> check(HW_SOURCE_FILE, 'product_using_accumulate',
    ...       ['Recursion', 'For', 'While'])
    True
    """
    "*** YOUR CODE HERE ***"
    return _______

Use OK to test your code:

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

Question 5: Filtered Accumulate

Show how to extend the accumulate function to allow for filtering the results produced by its term argument, by implementing the filtered_accumulate function in terms of accumulate:

def filtered_accumulate(combiner, base, pred, n, term):
    """Return the result of combining the terms in a sequence of N terms
    that satisfy the predicate PRED.  COMBINER is a two-argument function.
    If v1, v2, ..., vk are the values in TERM(1), TERM(2), ..., TERM(N)
    that satisfy PRED, then the result is
         BASE COMBINER v1 COMBINER v2 ... COMBINER vk
    (treating COMBINER as if it were a binary operator, like +). The
    implementation uses accumulate.

    >>> filtered_accumulate(add, 0, lambda x: True, 5, identity)  # 0 + 1 + 2 + 3 + 4 + 5
    15
    >>> filtered_accumulate(add, 11, lambda x: False, 5, identity) # 11
    11
    >>> filtered_accumulate(add, 0, odd, 5, identity)   # 0 + 1 + 3 + 5
    9
    >>> filtered_accumulate(mul, 1, greater_than_5, 5, square)  # 1 * 9 * 16 * 25
    3600
    >>> # Do not use while/for loops or recursion
    >>> from construct_check import check
    >>> check(HW_SOURCE_FILE, 'filtered_accumulate',
    ...       ['While', 'For', 'Recursion'])
    True
    """
    def combine_if(x, y):
        "*** YOUR CODE HERE ***"
    return accumulate(combine_if, base, n, term)

def odd(x):
    return x % 2 == 1

def greater_than_5(x):
    return x > 5

filtered_accumulate(combiner, base, pred, n, term) takes the following arguments:

  • combiner, base, term and n: the same arguments as accumulate.
  • pred: a one-argument predicate function applied to the values of term(k), k from 1 to n. Only values for which pred returns a true value are combined to form the result. If no values satisfy pred, then base is returned.

For example, filtered_accumulate(add, 0, is_prime, 11, identity) would be

0 + 2 + 3 + 5 + 7 + 11

for a suitable definition of is_prime.

Implement filtered_accumulate by defining the combine_if function. Exactly what this function does is something for you to discover. Do not write any loops or recursive calls to filtered_accumulate.

Use OK to test your code:

python3 ok -q filtered_accumulate

Question 6: Repeated

Implement a function repeated so that repeated(f, n)(x) returns f(f(...f(x)...)), where f is applied n times. That is, repeated(f, n) returns another function that can then be applied to another argument. For example, repeated(square, 3)(42) evaluates to square(square(square(42))). Yes, it makes sense to apply the function zero times! See if you can figure out a reasonable function to return for that case.

def repeated(f, n):
    """Return the function that computes the nth application of f.

    >>> add_three = repeated(increment, 3)
    >>> add_three(5)
    8
    >>> repeated(triple, 5)(1) # 3 * 3 * 3 * 3 * 3 * 1
    243
    >>> repeated(square, 2)(5) # square(square(5))
    625
    >>> repeated(square, 4)(5) # square(square(square(square(5))))
    152587890625
    >>> repeated(square, 0)(5)
    5
    """
    "*** YOUR CODE HERE ***"

For an extra challenge, try defining repeated using compose1 and your accumulate function in a single one-line return statement.

def compose1(f, g):
    """Return a function h, such that h(x) = f(g(x))."""
    def h(x):
        return f(g(x))
    return h

Use OK to test your code:

python3 ok -q repeated