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)("cs111!")
msg2 = composer(happy, sad)("is303!")
🤔 What do you think will happen?
One of the composed functions could itself be an HOF...
def happy(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!')
A higher-order function could return a function that references its own name.
def print_sums(n):
print(n)
def next_sum(k):
return print_sums(n + k)
return next_sum
print_sums(1)(3)(5)
The call:
print_sums(1)(3)(5)
produces the same result as:
g1 = print_sums(1)
g2 = g1(3)
g2(5)
A call to print_sums(x)
returns a function that:
x
as a side-effect, andy
, will do the same thing, but with
x+y
instead of x
.
So these calls will...
g1
,g2
,
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