Due by 11:59pm on Thursday, 9/22

## Instructions

Download hw04.zip. The vitamin problems can be found in the `vitamin` directory and the homework problems can be found in the `problems` directory. You must run ```python3 ok --submit``` twice: once inside the `vitamin` directory, and once inside the `problems` directory.

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:

## Vitamins

### Question 1: Taxicab Distance

An intersection in midtown Manhattan can be identified by an avenue and a street, which are both indexed by positive integers. The Manhattan distance or taxicab distance between two intersections is the number of blocks that must be traversed to reach one from the other, ignoring one-way street restrictions and construction. For example, Times Square is on 46th Street and 7th Avenue. Ess-a-Bagel is on 51st Street and 3rd Avenue. The taxicab distance between them is 9 blocks.

Implement `taxicab`, which computes the taxicab distance between two intersections using the following data abstraction. Hint: You don't need to know what a Cantor pairing function is; just use the abstraction.

``````def intersection(st, ave):
"""Represent an intersection using the Cantor pairing function."""
return (st+ave)*(st+ave+1)//2 + ave

def street(inter):
return w(inter) - avenue(inter)

def avenue(inter):
return inter - (w(inter) ** 2 + w(inter)) // 2

w = lambda z: int(((8*z+1)**0.5-1)/2)

def taxicab(a, b):
"""Return the taxicab distance between two intersections.

>>> times_square = intersection(46, 7)
>>> ess_a_bagel = intersection(51, 3)
>>> taxicab(times_square, ess_a_bagel)
9
>>> taxicab(ess_a_bagel, times_square)
9
"""
``````

Use OK to test your code:

``python3 ok -q taxicab``

### Question 2: 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. Try using a list comprehension.

``````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)
[]
"""
``````

Use OK to test your code:

``python3 ok -q squares``

## Required questions

### Question 3: G function

A mathematical function `G` on positive integers is defined by two cases:

``````G(n) = n,                                       if n <= 3
G(n) = G(n - 1) + 2 * G(n - 2) + 3 * G(n - 3),  if n > 3``````

Write a recursive function `g` that computes `G(n)`. Then, write an iterative function `g_iter` that also computes `G(n)`:

``````def g(n):
"""Return the value of G(n), computed recursively.

>>> g(1)
1
>>> g(2)
2
>>> g(3)
3
>>> g(4)
10
>>> g(5)
22
>>> from construct_check import check
>>> check(HW_SOURCE_FILE, 'g', ['While', 'For'])
True
"""

def g_iter(n):
"""Return the value of G(n), computed iteratively.

>>> g_iter(1)
1
>>> g_iter(2)
2
>>> g_iter(3)
3
>>> g_iter(4)
10
>>> g_iter(5)
22
>>> from construct_check import check
>>> check(HW_SOURCE_FILE, 'g_iter', ['Recursion'])
True
"""
``````

Use OK to test your code:

``````python3 ok -q g
python3 ok -q g_iter``````

### Question 4: Ping pong

The ping-pong sequence counts up starting from 1 and is always either counting up or counting down. At element `k`, the direction switches if `k` is a multiple of 7 or contains the digit 7. The first 30 elements of the ping-pong sequence are listed below, with direction swaps marked using brackets at the 7th, 14th, 17th, 21st, 27th, and 28th elements:

``1 2 3 4 5 6 [7] 6 5 4 3 2 1 [0] 1 2 [3] 2 1 0 [-1] 0 1 2 3 4 [5] [4] 5 6``

Implement a function `pingpong` that returns the nth element of the ping-pong sequence. Do not use any assignment statements; however, you may use `def` statements.

Hint: If you're stuck, try implementing `pingpong` first using assignment and a `while` statement. Any name that changes value will become an argument to a function in the recursive definition.

``````def pingpong(n):
"""Return the nth element of the ping-pong sequence.

>>> pingpong(7)
7
>>> pingpong(8)
6
>>> pingpong(15)
1
>>> pingpong(21)
-1
>>> pingpong(22)
0
>>> pingpong(30)
6
>>> pingpong(68)
2
>>> pingpong(69)
1
>>> pingpong(70)
0
>>> pingpong(71)
1
>>> pingpong(72)
0
>>> pingpong(100)
2
>>> from construct_check import check
>>> check(HW_SOURCE_FILE, 'pingpong', ['Assign', 'AugAssign'])
True
"""
``````

You may use the function `has_seven`, which returns True if a number `k` contains the digit 7 at least once.

``````def has_seven(k):
"""Returns True if at least one of the digits of k is a 7, False otherwise.

>>> has_seven(3)
False
>>> has_seven(7)
True
>>> has_seven(2734)
True
>>> has_seven(2634)
False
>>> has_seven(734)
True
>>> has_seven(7777)
True
"""
if k % 10 == 7:
return True
elif k < 10:
return False
else:
return has_seven(k // 10)``````

Use OK to test your code:

``python3 ok -q pingpong``

### Question 5: Count change

Once the machines take over, the denomination of every coin will be a power of two: 1-cent, 2-cent, 4-cent, 8-cent, 16-cent, etc. There will be no limit to how much a coin can be worth.

A set of coins makes change for `n` if the sum of the values of the coins is `n`. For example, the following sets make change for `7`:

• 7 1-cent coins
• 5 1-cent, 1 2-cent coins
• 3 1-cent, 2 2-cent coins
• 3 1-cent, 1 4-cent coins
• 1 1-cent, 3 2-cent coins
• 1 1-cent, 1 2-cent, 1 4-cent coins

Thus, there are 6 ways to make change for `7`. Write a function `count_change` that takes a positive integer `n` and returns the number of ways to make change for `n` using these coins of the future:

``````def count_change(amount):
"""Return the number of ways to make change for amount.

>>> count_change(7)
6
>>> count_change(10)
14
>>> count_change(20)
60
>>> count_change(100)
9828
"""
``````

Hint: you may find it helpful to refer to the implementation of `count_partitions`.

Use OK to test your code:

``python3 ok -q count_change``

## Extra questions

Extra questions are not worth extra credit and are entirely optional. They are designed to challenge you to think creatively!

### Question 6: Anonymous factorial

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 in particular that 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
>>> check(HW_SOURCE_FILE, 'make_anonymous_factorial', ['Assign', 'AugAssign', 'FunctionDef', 'Recursion'])
True
"""
return 'YOUR_EXPRESSION_HERE'
``````

Use OK to test your code:

``python3 ok -q make_anonymous_factorial``