#
Homework 4

*Due at 11:59:59 pm on Friday, February 26.*

## Instructions

Download hw04.zip. Inside the archive, you will find starter files for the questions in this homework, 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 this article for more instructions on okpy and submitting assignments.

**Readings:** This homework relies on following references:

## Lambdas

### Question 1: Make your own lambdas

For each of the following expressions, write functions `f1`

, `f2`

,
`f3`

, and `f4`

such that the evaluation of each expression
succeeds, without causing an error. Be sure to use lambdas in your
function definition instead of nested `def`

statements. Each function
should have a one line solution.

```
def f1():
"""
>>> f1()
3
"""
"*** YOUR CODE HERE ***"
def f2():
"""
>>> f2()()
3
"""
"*** YOUR CODE HERE ***"
def f3():
"""
>>> f3()(3)
3
"""
"*** YOUR CODE HERE ***"
def f4():
"""
>>> f4()()(3)()
3
"""
"*** YOUR CODE HERE ***"
```

Use OK to test your code:

```
python3 ok -q f1
python3 ok -q f2
python3 ok -q f3
python3 ok -q f4
```

### Question 2: Lambdas and Currying

We can transform multiple-argument functions into a chain of single-argument, higher order functions by taking advantage of lambda expressions. This is useful when dealing with functions that take only single-argument functions. We will see some examples of these later on.

Write a function `lambda_curry2`

that will curry any two argument
function using lambdas. See the doctest if you're not sure what this
means.

```
def lambda_curry2(fn):
"""
Returns a Curried version of a two argument function func.
>>> from operator import add
>>> x = lambda_curry2(add)
>>> y = x(3)
>>> y(5)
8
"""
"*** YOUR CODE HERE ***"
```

Use OK to test your code:

`python3 ok -q lambda_curry2`

## Politican (ADTs)

### Question 3

For this question, let's combine ADTs and dictionaries! Let's try to make an ADT that represents a politician. The `politician`

ADT has a constructor `make_politician`

. There are three selectors, `get_name`

, `get_party`

, and `get_age`

. When implementing an ADT, you have the freedom to choose how you want to represent it. Some common data structures used to represent ADTs are lists and dictionaries. The `city`

ADT was implemented using a list. Now, let's use dictionaries to implement our `politician`

ADT. You must use a dictionary for this question, or else you will not pass the tests.

Use OK to test your code:

```
python3 ok -q make_politician
python3 ok -q get_pol_name
python3 ok -q get_party
python3 ok -q get_age
```

## Dice Rolling (ADTs)

**Introduction.** Brian and Shreya are playing a dice game. This dice game is special in that players choose the range of one of their dice, as well as one of their opponents. Unfortunately, both Brian and Shreya forgot to bring dice to their game - all they have is their laptop, and they need you to build an abstract object to simulate a die. Given the smallest and largest values of the die, you can construct the die using the dice constructor.

### Question 4

In order to create the dice object, create a list of all the values that the dice can take on.

```
import random
random.seed(42)
def dice(x, y):
"""Construct a die that is a list from x to y inclusive.
>>> dice(1, 6)
[1, 2, 3, 4, 5, 6]
>>> dice(3, 5)
[3, 4, 5]
>>> dice(5, 5)
[5]
"""
"*** YOUR CODE HERE ***"
def smallest(die):
"""Return the lowest value die can take on."""
return min(die)
def largest(die):
"""Return the largest value die can take on."""
return max(die)
def str_dice(die):
"""Return a string representation of die.
>>> str_dice(dice(1, 6))
'die takes on values from 1 to 6'
"""
return 'die takes on values from {0} to {1}'.format(smallest(die), largest(die))
```

Use OK to test your code:

`python3 ok -q dice`

### Question 5

Die rolls are inherently random - before rolling, we do not know what value the die will take on. We need to implement this feature in order to play the game. Python has a nice library that allows us to generate random numbers. You can read more about it here. We will be specifically using `random.choice(seq)`

, which generates a random integer from a nonempty sequence `seq`

.

```
def roll_dice(die, a):
"""Roll the die a times and return an array of the rolled values.
>>> roll_dice(dice(5, 5), 4)
[5, 5, 5, 5]
>>> max(roll_dice(dice(1, 6), 100))
6
>>> min(roll_dice(dice(1, 6), 100))
1
>>> x = sum(roll_dice(dice(1, 6), 1000))/1000 # Finds the mean of 1000 dice rolls
>>> 3 <= x <= 4 # Checks if the mean is between 3 and 4
True
"""
"*** YOUR CODE HERE ***"
```

Use OK to test your code:

`python3 ok -q roll_dice`

### Question 6

Rolling a six is unfortunate in this game. Neither player wants a 6, yet they cannot avoid it. Figure out how many rolls it takes until a player rolls a 6 with a certain die.

```
def rolls_until_six(die):
"""Roll the die until you get a 6 and return the number of rolls it took to do so.
If six is not a the possible values to roll, return a string saying '6 is not a possible value of this die'
>>> rolls_until_six(dice(1, 5))
'6 is not a possible value of this die'
>>> rolls_until_six(dice(6, 6)) # Takes one roll to get 6
1
>>> x = sum([rolls_until_six(dice(1, 6)) for _ in range(1000)])/1000 # Repeat 1000 times and average
>>> 5 <= x <= 7 # Check that it takes between 5 and 7 rolls overall on average
True
"""
"*** YOUR CODE HERE ***"
```

Use OK to test your code:

`python3 ok -q rolls_until_six`

### Question 7

The game has a new complication - instead of rolling a single die at a time, players are required to roll multiple dice at a time. Players start off with 2 dice in a cup, but they can add more.

```
def cup(die_1, die_2):
"""Construct a cup that contains die1 and die2.
>>> cup(dice(1, 1), dice(1, 2))
[[1], [1, 2]]
"""
"*** YOUR CODE HERE ***"
```

Use OK to test your code:

`python3 ok -q cup`

### Question 8

Add a die to your cup implementation!

```
def add_to_cup(cup, die):
"""Add die to cup.
>>> cup1 = cup(dice(1, 1), dice(1, 2))
>>> add_to_cup(cup1, dice(1, 3))
[[1], [1, 2], [1, 2, 3]]
"""
"*** YOUR CODE HERE ***"
```

Use OK to test your code:

`python3 ok -q add_to_cup`

### Question 9

When you roll a cup, you roll each dice in the cup once, then return an array of the rolled values.

```
def roll_cup(cup):
"""Roll every die in the cup and return an array of the rolled values.
>>> roll_cup(cup(dice(1, 1), dice(2, 2)))
[1, 2]
"""
"*** YOUR CODE HERE ***"
```

Use OK to test your code:

`python3 ok -q roll_cup`