What happens? | ||
---|---|---|
Def statement |
|
|
Call expression |
|
|
Calling/applying |
4 ▶
def square( x )
▶ 16
|
|
def square(x):
return x * x
square(square(3))
square | ⚫️ | ----> func square(x) [parent=Global] |
x | 3 | |
Return value | 9 |
x | 9 | |
Return value | 81 |
def square(x):
return x * x
square(square(3))
square | ⚫️ | ----> func square(x) [parent=Global] |
x | 3 | |
Return value | 9 |
x | 9 | |
Return value | 81 |
An environment is a sequence of frames.
def square(x):
return x * x
square(square(3))
square | ⚫️ | ----> func square(x) [parent=Global] |
x | 3 | |
Return value | 9 |
x | 9 | |
Return value | 81 |
Every expression is evaluated in the context of an environment.
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 square(square):
return square * square
square(4)
square | ⚫️ | ----> func square(square) [parent=Global] |
square | 4 | |
Return value | 16 |
Every expression is evaluated in the context of an environment.
A name evaluates to the value bound to that name in the earliest frame of the current environment in which that name is found.
summation(5, lambda x: x**2)
make_adder(3)(1)
Functions are first class: Functions are values in Python.
def apply_twice(f, x):
return f(f(x))
def square(x):
return x ** 2
apply_twice(square, 3)
def make_texter(emoji):
def texter(text):
return emoji + text + emoji
return texter
happy_text = make_texter("😄")
result = happy_text("lets go to the beach!")
func <name>(<formal parameters>) [parent=<label>]
<name>
to the function value in the current frame
<name>
of the function being called.[parent=>label<]
<formal parameters>
to the arguments in the local frame.
def thingy(x, y):
return bobber(y)
def bobber(a):
return a + y
result = thingy("ma", "jig")
🤔 What do you think will happen?
Local names are not visible to other (non-nested) functions.
def happy(text):
return "☻" + text + "☻"
def sad(text):
return "☹" + text + "☹"
def composer(f, g):
def composed(x):
return f(g(x))
return composed
msg1 = composer(sad, happy)("cs61a!")
msg2 = composer(happy, sad)("eecs16a!")
🤔 What do you think will happen?
One of the composed functions could itself be an HOF...
def happy(text):
return "☻" + text + "☻"
def sad(text):
return "☹" + text + "☹"
def make_texter(emoji):
def texter(text):
return emoji + text + emoji
return texter
def composer(f, g):
def composed(x):
return f(g(x))
return composed
composer(happy, make_texter("☃︎"))('snow day!')
Compare...
from operator import add
add(2, 3)
def make_adder(n):
return lambda x: n + x
make_adder(2)(3)
🤔 What's the relationship between add(2, 3)
and make_adder(2)(3)
?
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
make_adder = curry2(add)
make_adder(2)(3)
curry2 = lambda f: lambda x: lambda y: f(x, y)
It's not food! ❌ 🥘 ❌ 🍛
Named after American logician Haskell Curry, but actually published first by Russian Moses Schönfinkel, based on principles by German Gottlob Frege.
See also: Stigler's law of eponymy