Currying: Converting a function that takes multiple arguments into a singleargument higherorder function.
A function that currys any twoargument function:
def curry2(f):
def g(x):
def h(y):
return f(x, y)
return h
return g
from operator import add
make_adder = curry2(add)
make_adder(2)(3)
curry2 = lambda f: lambda x: lambda y: f(x, y)
Whenever another function requires a function that only takes one argument:
def transform_numbers(num1, num2, num3, transform):
return transform(num1), transform(num2), transform(num3)
transform_numbers(3, 4, 5, curry2(add)(60))
Alternate approach:
transform_numbers(3, 4, 5, lambda x: add(60, x))
Turning a generalized function into a specialized function:
def html_tag(tag_name, text):
return "<" + tag_name + ">" + text + "</" + tag_name + ">"
p_tag = curry2(html_tag)("p")
p_tag("hello hello")
Alternate approach:
import functools
p_tag = functools.partial(html_tag, "p")
p_tag("hello hello")
ðŸ¥¦ It's good for you!
CS61A introduces many concepts that aren't standard Python practice, but that show up in other languages.
Currying is a very common practice in functional programming languages like Haskell or Clojure.
Let's make a higherorder tracing function.
def trace1(f):
"""Return a function that takes a single argument, x, prints it,
computes and prints F(x), and returns the computed value.
>>> square = lambda x: x * x
>>> trace1(square)(3)
> 3
< 9
9
"""
def traced(x):
print(">", x)
r = f(x)
print("<", r)
return r
return traced
What if we always wanted a function to be traced?
@trace1
def square(x):
return x * x
That's equivalent to..
def square(x):
return x * x
square = trace1(square)
The notation:
@ATTR
def aFunc(...):
...
is essentially equivalent to:
def aFunc(...):
...
aFunc = ATTR(aFunc)
ATTR
can be any expression, not just a single function name.
WWPD exercises test our understanding of how Python evaluates code and what it chooses to display in the shell.
The expression  Evaluates to  Interactive output 

5
 5
 5

print(5)
 None
 5

print(print(5))
 None
 5 None

>> 5
5
>>> print(5)
5
>>> print(print(5))
5
None
def delay(arg):
print('delayed')
def g():
return arg
return g
The expression  Evaluates to  Interactive output 

delay(6)()
 6
 delayed 6

delay(delay)()(6)()
 6

delayed delayed 6

print(delay(print)()(4))
 None

delayed 4 None

def pirate(arggg):
print('matey')
def plunder(arggg):
return arggg
return plunder
The expression  Evaluates to  Interactive output 

pirate('treasure')('scurvy')
 'scurvy'
 matey 'scurvy'

add(pirate(3)(square)(4), 1)
 17

matey 17

pirate(pirate(pirate))(5)(7)
 Error

matey matey Error

A name evaluates to the value bound to that name in the earliest frame of the current environment in which that name is found.
def horse(mask):
horse = mask
def mask(horse):
return horse
return horse(mask)
mask = lambda horse: horse(2)
horse(mask)
horse  
mask  
Return value 
Return value 
Return value 
def remove(n, digit):
"""Return digits of nonnegative N
that are not DIGIT, for some
nonnegative DIGIT less than 10.
>>> remove(231, 3)
21
>>> remove(243132, 2)
4313
"""
kept = 0
digits = 0
while ___________________________:
last = n % 10
n = n // 10
if __________________________:
kept = __________________
digits = ________________
return ___________________________
def remove(n, digit):
"""Return digits of nonnegative N
that are not DIGIT, for some
nonnegative DIGIT less than 10.
>>> remove(231, 3)
21
>>> remove(243132, 2)
4313
"""
kept = 0
digits = 0
while n > 0:
last = n % 10
n = n // 10
if last != digit:
kept = kept + (last * 10 ** digits)
digits = digits + 1
return kept