Due at 11:59pm on 06/30/2015.

Starter Files

Download lab03.zip. Inside the archive, you will find starter files for the questions in this lab, along with a copy of the OK autograder.

Submission

By the end of this lab, you should have submitted the lab with python3 ok --submit. You may submit more than once before the deadline; only the final submission will be graded.

  • To receive credit for this lab, you must complete Questions 4, 5, and 6 in lab03.py and submit through OK.
  • Questions 1, 2, 3, 7, 8, (What Would Python Print?), 9 and 10 (Environment Diagrams) are designed to help introduce concepts and test your understanding.
  • Questions 11, 12, and 13 are optional extra practice (all except 11 are in lab03_extra.py). It is recommended that you complete these problems on your own time.

Higher Order Functions

Higher order functions are functions that take a function as an input, and/or output a function. We will be exploring many applications of higher order functions.

Question 1: What would Python print?

>>> def square(x):
...     return x * x
...
>>> def neg(f, x):
...     return -f(x)
...
>>> neg(square, 4)
______
-16

Question 2: What would Python print?

>>> def even(f):
...     def odd(x):
...         if x < 0:
...             return f(-x)
...         return f(x)
...     return odd
...
>>> def identity(x):
...     return x
...
>>> triangle = even(identity)
>>> triangle
______
<function ...>
>>> triangle(61)
______
61
>>> triangle(-4)
______
4

Question 3: What would Python print?

>>> def first(x):
...     x += 8
...     def second(y):
...         print('second')
...         return x + y
...     print('first')
...     return second
...
>>> f = first(15)
______
first
>>> f
______
<function ...>
>>> f(16)
______
second 39

Question 4: Temperature Converter

Write a function that converts Fahrenheit to Celsius and another function that converts Celsius to Fahrenheit.

The formulas are as follows:

  • Celsius x 9 / 5 + 32 = Fahrenheit
  • (Fahrenheit - 32) x 5 / 9 = Celsius
def f_to_c(fahrenheit):
    """Converts Fahrenheit to Celsius

    >>> f_to_c(14)
    -10.0
    >>> f_to_c(68)
    20.0
    >>> f_to_c(-31)
    -35.0
    """
"*** YOUR CODE HERE ***"
return (fahrenheit - 32) * 5 / 9
def c_to_f(celsius): """Converts Celsius to Fahrenheit >>> c_to_f(0) 32.0 >>> c_to_f(5) 41.0 >>> c_to_f(-25) -13.0 """
"*** YOUR CODE HERE ***"
return (celsius) * 9 / 5 + 32

Use OK to test your code:

python3 ok -q f_to_c
python3 ok -q c_to_f

Question 5: Temperature Converters Combined!

Implement dispatch_function, which takes in two functions (f1 and f2) and two strings (option1 and option2). dispatch_function returns a function that does the following:

  • Takes an option (a string) and a number as its two parameters
  • Asserts that option is either option1 or option2 (using an assert statement)
  • Calls the corresponding function (f1 or f2) on the given number

An assert statement checks if a statement is true. If it is false it will raise an error. This is a quick way to check for unexpected inputs. For example, the following assert statement ensures x won't be zero.

def no_zero_division(x):
    assert x != 0
    return 2 / x

If no_zero_division is called with x = 0, an AssertionError occurs:

>>> no_zero_division(0):
AssertionError
def dispatch_function(option1, f1, option2, f2):
    """Takes in two options and two functions. Returns a function that takes in
    an option and value and calls either f1 or f2 depending on the given option.

    >>> func_d = dispatch_function('c to f', c_to_f, 'f to c', f_to_c)
    >>> func_d('c to f', 0)
    32.0
    >>> func_d('f to c', 68)
    20.0
    >>> func_d('blabl', 2)
    AssertionError
    """
"*** YOUR CODE HERE ***"
def func(option, value): assert option == option1 or option == option2 if option == option1: return f1(value) else: return f2(value) return func

Use OK to test your code:

python3 ok -q dispatch_function

Question 6: Flight of the Bumblebee

Write a function that takes in a number n and returns a function that takes in a number range which will print all numbers from 0 to range (including 0 but excluding range) but print Buzz! instead for all the numbers that are divisible by n.

def make_buzzer(n):
    """ Returns a function that prints numbers in a specified
    range except those divisible by n.

    >>> i_hate_fives = make_buzzer(5)
    >>> i_hate_fives(10)
    Buzz!
    1
    2
    3
    4
    Buzz!
    6
    7
    8
    9
    """
"*** YOUR CODE HERE ***"
def buzz(m): i = 0 while i < m: if i % n == 0: print('Buzz!') else: print(i) i += 1 return buzz

Use OK to test your code:

python3 ok -q make_buzzer

Lambdas

Lambda expressions are one-line functions that specify two things: the parameters and the return value.

lambda <parameters>: <return value>

While both lambda and def statements are related to functions, there are some differences.

lambda def
Type lambda is an expression def is a statement
Description Evaluating a lambda expression does not create or modify any variables. Lambda expressions just create function objects. Executing a def statement will create a new function object and binded to a variable in the current environment.
Example
lambda x: x * x
         
def square(x):
      return x * x

A lambda expression by itself is not very interesting. As with any objects such as numbers, booleans, strings, we usually:

  • assign lambda to variables (foo = lambda x: x)
  • pass them in to other functions (bar(lambda x: x))

Question 7: What Would Python print?

>>> a = lambda x: x
>>> a(5) # x is the argument for the lambda function
______
5
>>> b = lambda: 3 >>> b()
______
3
>>> c = lambda x: lambda: print("123") >>> c(88)
______
function lambda at ...
>>> c(88)()
______
123
>>> d = lambda f: f(4) # They can take in functions as well. >>> def square(x): ... return x * x >>> d(square)
______
16

Question 8: What would Python print?

>>> t = lambda f: lambda x: f(f(f(x)))
>>> s = lambda x: x + 1
>>> t(s)(0)
______
3
>>> bar = lambda y: lambda x: 16 >>> bar()(15)
______
TypeError: <lambda>() missing 1 required positional argument: 'y'
>>> lambda x: x # Can we access this function?
______
<function <lambda> at ...>
>>> foo = lambda: 32 >>> foobar = lambda x,y : x // y >>> a = lambda x: foobar(foo(), bar(10)(x)) >>> a(2)
______
2
>>> b = lambda x,y: print('summer') # When is the body of this function run?
______
# Nothing gets printed by the interpreter
>>> c = b(4, 'dog')
______
'summer'
>>> print(c)
______
None

Question 9: Environment Diagrams with Lambdas

Try drawing environment diagrams for the following code and predicting what Python will output.

You can check your work with the Online Python Tutor. Please try drawing it yourself first!

>>> # Part 1
>>> a = lambda x : x * 2 + 1
>>> def b(x):
...     return x * y
...
>>> y = 3
>>> b(y)
______
9
>>> def c(x): ... y = a(x) ... return b(x) + a(x+y) ... >>> c(y)
______
30

Question 10: More Environment Diagrams with Lambdas

Try drawing environment diagrams for the following code and predicting what Python will output.

You can check your work with the Online Python Tutor. Please try drawing it yourself first!

>>> # This one is pretty tough. A carefully drawn environment
>>> # diagram will be really useful.
>>> g = lambda x: x + 3
>>> def wow(f):
...     def boom(g):
...       return f(g)
...     return boom
...
>>> f = wow(g)
>>> f(2)
______
5
>>> g = lambda x: x * x >>> f(3)
______
6

Extra Questions

Questions in this section are not required for submission. However, we encourage you to try them out on your own time for extra practice.

Question 11: 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(func):
    """
    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 ***" return ______
return lambda arg1: lambda arg2: func(arg1, arg2)

Use OK to test your code:

python3 ok -q lambda_curry2

Question 12: Funception

Write a function (funception) that takes in another function func_a and a number start and returns a function (func_b) that will have one parameter to take in the stop value. func_b should take the following into consideration the following in order:

  1. Takes in the stop value.
  2. If the value of start is less than 0, it should exit the function.
  3. If the value of start is greater than stop, apply func_a on start and return the result.
  4. If not, apply func_a on all the numbers from start (inclusive) up to stop (exclusive) and return the product.
def funception(func_a, start):
    """ Takes in a function (function A) and a start value.
    Returns a function (function B) that will find the product of 
    function A applied to the range of numbers from 
    start (inclusive) to stop (exclusive)

    >>> def func_a(num):
    ...     return num + 1
    >>> func_b1 = funception(func_a, 3)
    >>> func_b1(2)
    4
    >>> func_b2 = funception(func_a, -2)
    >>> func_b2(-3)
    >>> func_b3 = funception(func_a, -1)
    >>> func_b3(4)
    >>> func_b4 = funception(func_a, 0)
    >>> func_b4(3)
    6
    >>> func_b5 = funception(func_a, 1)
    >>> func_b5(4)
    24
    """
"*** YOUR CODE HERE ***"
def func_b(stop): i = start product = 1 if start < 0: return None if start > stop: return func_a(start) while i < stop: product *= func_a(i) i += 1 return product return func_b

Use OK to test your code:

python3 ok -q funception

Question 13: I Heard You Liked Functions...

Define a function cycle that takes in three functions f1, f2, f3, as arguments. cycle will return another function that should take in an integer argument n and return another function. That final function should take in an argument x and cycle through applying f1, f2, and f3 to x, depending on what n was. Here's the what the final function should do to x for a few values of n:

  • n = 0, return x
  • n = 1, apply f1 to x, or return f1(x)
  • n = 2, apply f1 to x and then f2 to the result of that, or return f2(f1(x))
  • n = 3, apply f1 to x, f2 to the result of applying f1, and then f3 to the result of applying f2, or f3(f2(f1(x)))
  • n = 4, start the cycle again applying f1, then f2, then f3, then f1 again, or f1(f3(f2(f1(x))))
  • And so forth.

Hint: most of the work goes inside the most nested function.

def cycle(f1, f2, f3):
    """ Returns a function that is itself a higher order function
    >>> def add1(x):
    ...     return x + 1
    >>> def times2(x):
    ...     return x * 2
    >>> def add3(x):
    ...     return x + 3
    >>> my_cycle = cycle(add1, times2, add3)
    >>> identity = my_cycle(0)
    >>> identity(5)
    5
    >>> add_one_then_double = my_cycle(2)
    >>> add_one_then_double(1)
    4
    >>> do_all_functions = my_cycle(3)
    >>> do_all_functions(2)
    9
    >>> do_more_than_a_cycle = my_cycle(4)
    >>> do_more_than_a_cycle(2)
    10
    >>> do_two_cycles = my_cycle(6)
    >>> do_two_cycles(1)
    19
    """
"*** YOUR CODE HERE ***"
def ret_fn(n): def ret(x): i = 0 while i < n: if i % 3 == 0: x = f1(x) elif i % 3 == 1: x = f2(x) else: x = f3(x) i += 1 return x return ret return ret_fn

Use OK to test your code:

python3 ok -q cycle