# Function Examples

Tips for navigating the slides:
• Press O or Escape for overview mode.
• Visit this link for a nice printable version
• Press the copy icon on the upper right of code blocks to copy the code

• Currying
• Decorators
• Review

## Currying

### (Reminder) Function currying

Currying: Converting a function that takes multiple arguments into a single-argument higher-order function.

A function that currys any two-argument function:

``````
def curry2(f):
def g(x):
def h(y):
return f(x, y)
return h
return g
``````
``````

``````
``````
curry2 = lambda f: lambda x: lambda y: f(x, y)
``````

### Use case for currying #1

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

Alternate approach:

``````
transform_numbers(3, 4, 5, lambda x: add(60, x))
``````

### Use case for currying #2

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

### Why learn currying in Python?

🥦 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.

## Decorators

### A tracing function

Let's make a higher-order 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
``````

### A tracing decorator

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

### General decorator syntax

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.

## Review

### What Would Python Do? #1

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

### What Would Python Do? #2

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

### What Would Python Do? #3

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

### Environment Diagram

``````
return horse

``````
Global frame
f1:
 Return value
f2:
 Return value
f3:
 Return value

### Implementing a function

``````
def remove(n, digit):
"""Return digits of non-negative N
that are not DIGIT, for some
non-negative 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 ___________________________
``````
• Verify the examples & pick a simple one
• Implement without the template, then change your implementation to match the template.
OR If the template is helpful, use it.
• Annotate names with values from your chosen example
• Write code to compute the result
• Did you really return the right thing?
• Check your solution with the other examples

### Implementing a function

``````
def remove(n, digit):
"""Return digits of non-negative N
that are not DIGIT, for some
non-negative 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
``````