Higher Order Functions are enabled by Environments
Higher-order Function: A function that takes a function as an argument or returns a function as a return value.
1
2
3
4
5
6
7
def apply_twice(f, x):
return f(f(x))
def square(x):
return x * x
apply_twice(square, 3)
1
81
Runtime
- The functions
apply_twiceandsquareare bound to their names in the global frame - apply_twice is called, the argument
xis stored as 3, andfis stored assquare, a new frame is created - We evaluate the body, of
apply_twice. We see that the function returns the output of functionf. - We first evaluate the result of operand, which is
f(x). We evaluate the body off=squareand create a new frame wherexis stored as 3 - The operand returns a value of
9to our outer function. - We then evaluate the operator on the operand, which is
f(9). We create another frame and evaluate f(9), which finally returns 81, which is also returned by apply_twice. - The return value of
apply_twiceis finally stored under the name ofresultin the global frame.
Environments for Nested Definition
1
2
3
4
5
6
7
def make_adder(n):
def adder(k):
return k + n
return adder
add_three = make_adder(3)
add_three(4)
1
7
Runtime
- The name
make_adderis bound to its function make_adder(3)is called. A new frame formake_adder(3)is created with a parent as the Global Frame.- In the new frame, the name
nis bound to 3, whiledef adder(k):binds the nameadderto the function. make_adder(3)then returnsadderas its return value, which is bound to the name ofadd_three.- We then execute
add_three(4)and a new frame is created, with a parent ofmake_adder - The value 4 is bound to
kin theadd_threeframe. add_threeaccess the value ofnbecause it is present in a later frame in an instance ofmake_adder.add_threereturns the final value ofnpluskwhich evaluates to 7.
Reflection
- By defining the parents of a function, we can easily trace out way back through our frames to access values from parent functions.
- In the example given above, we have an environment with 3 frames when we are executing the add_three function.
- All user-defined functions have a parent frame.
- The parent is the frame in which the function was defined.
How to Draw an Environment Diagram
- When a function is defined:
- Create a function value
func <name>(<formal parameters>) [parent=<parent>] - Parent is the current frame
- Function name is bound to function value in the frame where the function is defined
- Create a function value
- When a function is called:
- Add a local frame with the name of the function called.
- Copy the parent of the funciton to the local frame:
[parent=<label>] - Bind
<formal parameters>to the arguments within the local frame. - Execute function body in the environment that starts with the local frame.
Local Names
- Formal parameters of functions have a local name
- Local names cannot be refered by frame that are not a child of the frame or in a different environment than the frame.
Function Composition
1
2
3
4
5
6
7
8
9
10
11
12
13
def square(x):
return x * x
def triple(x):
return 3 * x
def compose1(f, g):
def h(x): # This is function composition
return f(g(x))
return h
squiple = compose1(square, triple)
squiple(5)
1
225