Study Guide: Environments and HOF

Instructions

This is a study guide with links to past lectures, assignments, and handouts, as well as additional practice problems to assist you in learning the concepts.

Draw environment diagrams automatically with Python Tutor.

Assignments

Important: For solutions to these assignments once they have been released, see the main website

Handouts

Lectures

Readings

Environment Diagrams

Albert Wu's Environment Diagrams Guide provides an alternative view of the rules and additional practice.

Environment diagrams help us visualize Python's process. For each line of code we should first identify what kind of statement or expression it is and then execute the line according to the rules.

A couple tips:

  • You can only bind names to values. No expressions (like 3 + 4) allowed on environment diagrams!
  • Frames and functions both have parents.
  • Primitive values which include booleans (True, False), numbers (42), and strings ('cs 61a') go directly in the box for the value of a binding.
  • Composite values and function values are drawn off to the side and require a pointer from the value box to the object.

Expressions

Primitive Expressions

  1. In the current environment return the value of the primitive expression. If the expression is just a number or a string, it evaluates to the Python representation of that number or string. If the expression is a name, look for the name in the current environment by following the lookup procedure by starting in the current frame and looking up each parent frame until the global frame.

Call Expressions

  1. Evaluate the operator, which could itself be another call expression.
  2. Evaluate the operands from left to right, each of which could be call expressions.
  3. Apply the operator (evaluated as a function) to the operands (evaluated as arguments). If this is a built-in function, we skip directly to the final result because we don't know how built-in functions are implemented. But if the function is user-defined, create a new frame in the environment diagram.
  4. Bind the formal parameters to the argument values (the evaluated operands).
  5. Evaluate the body of the function in the new frame.
  6. After evaluating the body of the function, return to the frame that called the function.

Statements

Assignment Statements

Note that assignment rules become more complicated with nonlocal.

  1. Evaluate the expression to the right of the assignment operator =.
  2. Bind the variable name to the value of the expression in the current frame. Be sure you override the variable name if it had a previous binding.

def Statements

  1. Draw the func name(arg1, arg2, ...).
  2. The parent of the function is wherever the function was defined (the frame we're currently in, since we're creating the function).
  3. Bind the name of the function to the function value in the current frame.

return Statements

  1. Evaluate the expression to the right of return.
  2. In the current frame, create a space for the return value which contains the value of the return expression.

Interactive Environment Diagram Questions

Important: You may have to log in to your okpy account to be able to modify the interactive diagrams below.

Basic

Q1: Environment Diagrams - Basic

These questions were originally developed by Albert Wu and are included here for extra practice. We recommend checking your work in PythonTutor after filling in the diagrams for the code below.

Q1a

Draw the environment diagram that results from executing the code below.

Guiding Question:

  • How does return behave differently than print?
def square1(x):
    return x * x

def square2(x):
    print(x * x)

a = square1(3)
b = square2(3)
Objects
Global frame
f1: [parent=]
Return value
f2: [parent=]
Return value
f3: [parent=]
Return value

Q1b

Draw the environment diagram that results from executing the code below.

Guiding Questions:

  • How many times do we call mul?
  • How many frames do we draw for mul?
def square(x):
    return x * x

def sum_of_squares(x, y):
    return square(x) + square(y)

result = sum_of_squares(3, 4)
Objects
Global frame
f1: [parent=]
Return value
f2: [parent=]
Return value
f3: [parent=]
Return value

Q1c

Draw the environment diagram that results from executing the code below.

Guiding Questions:

  • What changes between the first time we call add and the second time?
  • How does this affect our diagram?
from operator import add
first = add(3, 4)

def add(a, b):
    return a + b

second = add(3, 4)
Objects
Global frame
f1: [parent=]
Return value
f2: [parent=]
Return value
f3: [parent=]
Return value

Q1d

Draw the environment diagram that results from executing the code below.

Guiding Questions:

  • Did the global values of score and opp_score change?
score, opp_score = 0, 0

def assign(arg0, arg1):
    score = arg0
    opp_score = arg1
    return True

success = assign(3, 9001)
Objects
Global frame
f1: [parent=]
Return value
f2: [parent=]
Return value
f3: [parent=]
Return value

Q1e

Draw the environment diagram that results from executing the code below.

Guiding Questions:

  • What's the lookup procedure for goal?
  • Does result ever show up in the diagram?
goal = 100

def foo(x):
    y = x + goal
    return b

result = foo(4)
Objects
Global frame
f1: [parent=]
Return value
f2: [parent=]
Return value
f3: [parent=]
Return value

Q1f

Draw the environment diagram that results from executing the code below.

from operator import add, sub
def a_plus_abs_b(a, b):
    if b < 0:
        op = sub
    else:
        op = add
    return op(a, b)

result = a_plus_abs_b(4, -4)
Objects
Global frame
f1: [parent=]
Return value
f2: [parent=]
Return value
f3: [parent=]
Return value

Challenge

Q2: Environment Diagrams - Basic

These questions were originally developed by Albert Wu and are included here for extra practice. We recommend checking your work in PythonTutor after filling in the diagrams for the code below.

Q2c

Draw the environment diagram that results from executing the code below.

def fun(fun):
    def time(time):
        return fun(x)
    x = 4
    return time

def boo(x):
    return x**2
    x = 5

result = fun(boo)(10)
Objects
Global frame
f1: [parent=]
Return value
f2: [parent=]
Return value
f3: [parent=]
Return value

Q2d

Draw the environment diagram that results from executing the code below.

from operator import sub
def trick(me, you):
    sub = treat
    return sub

def treat(me, you):
    return sub(me, 1)

treat = trick
trick(3, 4)
Objects
Global frame
f1: [parent=]
Return value
f2: [parent=]
Return value
f3: [parent=]
Return value

Q2e

Draw the environment diagram that results from executing the code below.

def easy(x):
    def peasy(y):
        def ironic(name):
            return name(x, y)
        return y
    return peasy

result = easy(4)(easy)(2)
Objects
Global frame
f1: [parent=]
Return value
f2: [parent=]
Return value
f3: [parent=]
Return value

Additional Practice Problems

These questions provide practice with drawing environment diagrams from scratch. We recommend solving these on paper and checking your work in PythonTutor.

Easy

Q3: Make Adder

Draw the environment diagram for the following code.

>>> def adder_maker(x):
...     def adder(y):
...         return x + y
...     return adder
>>> add3 = adder_maker(3)
>>> add3(4)
______
7
>>> sub5 = adder_maker(-5) >>> sub5(6)
______
1
>>> sub5(10) == add3(2)
______
True

Q4: Alpaca Zebra

Draw the environment diagram for the following code.

def yak(zebra):
    return 20 // zebra

def llama(alpaca):
    zebra = 0
    def yak(zebra):
        return alpaca(zebra)
    return yak

llama(yak)(4)

Verify your solution with Python Tutor.

Q5: Multiplication

Using a lambda expression, complete the mul_by_num function. This function should take an argument and return a one argument function that multiplies any value passed to it by the original number.

def mul_by_num(num):
    """
    Returns a function that takes one argument and returns num
    times that argument.
    >>> x = mul_by_num(5)
    >>> y = mul_by_num(2)
    >>> x(3)
    15
    >>> y(-4)
    -8
    """
"*** YOUR CODE HERE ***" return ______
return lambda num2: num * num2

Medium

Q6: Community

>>> def troy():
...     abed = 0
...     while abed < 3:
...         britta = lambda: abed
...         print(abed)
...         abed += 2
...     annie = abed
...     annie += 1
...     abed = 6 # seasons and a movie
...     return britta
>>> jeff = troy()
______
0 2
>>> shirley = lambda: jeff >>> pierce = shirley() >>> pierce()
______
6

Q7: Doge

Draw the environment diagram for the following code.

wow = 6

def much(wow):
    if much == wow:
        such = lambda wow: 5
        def wow():
            return such
        return wow
    such = lambda wow: 4
    return wow()

wow = much(much(much))(wow)

You can check out what happens when you run the code block using Python Tutor. Please ignore the “ambiguous parent frame” message on step 18. The parent is in fact f1.

Q8: Two Kinds of People

Draw the environment diagram for the following code.

def blondie(f):
    return lambda x: f(x + 1)

tuco = blondie(lambda x: x * x)
angel_eyes = tuco(2)

Verify your solution with Python Tutor.

Q9: Reruns

>>> def troy():
...     abed = 0
...     while abed < 10:
...         britta = lambda: abed
...         abed += 1
...     abed = 20
...     return britta
...
>>> jeff = troy()
>>> shirley = lambda : jeff
>>> pierce = shirley()
>>> pierce()
______
20