# Homework 4

*Due by 11:59pm on Thursday, 9/21*

## Instructions

Download hw04.zip. Inside the archive, you will find a file called
hw04.py, along with a copy of the `ok`

autograder.

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

The `construct_check`

module is used in this assignment, which defines a
function `check`

. For example, a call such as

`check("foo.py", "func1", ["While", "For", "Recursion"])`

checks that the function `func1`

in file `foo.py`

does *not* contain
any `while`

or `for`

constructs, and is not an overtly recursive function (i.e.,
one in which a function contains a call to itself by name.)

## Required questions

### Q1: 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
"""
"*** YOUR CODE HERE ***"
```

Use Ok to test your code:

`python3 ok -q taxicab`

### Q2: 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)
[]
"""
"*** YOUR CODE HERE ***"
```

Use Ok to test your code:

`python3 ok -q squares`

### Q3: 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
"""
"*** YOUR CODE HERE ***"
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
"""
"*** YOUR CODE HERE ***"
```

Use Ok to test your code:

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

### Q4: 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
"""
"*** YOUR CODE HERE ***"
```

You may use the function `has_seven`

, which returns True if a number `k`

contains the digit 7 at least once.

Use Ok to test your code:

`python3 ok -q pingpong`

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

### Q5: 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
"""
"*** YOUR CODE HERE ***"
```

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!

### Q6: 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`