Lecture 10 Mutable Functions & Growth
These are my notes for SICP(Structure and Interpretation of Computer Programs). Hope they’ll be of some help to you.
Review: Mutable Values
Identity vs. equality in environment diagrams
- Review: For assignment statements, evaluate the rignt side, then assign to the left
- Copying: When creating a copy, copy exactly what’s “in the box”.
Mutating Within Functions
>>> def mystery(lst): #mutative function
... lst.pop()
... lst.pop()
>>> four = [4, 4, 4, 4]
>>> mystery(four)
>>> four
[4, 4]
Mutable Functions I
Functions with behavior that changes over time
- An immutable function returns the same value when called with the same input
- A mutable function returns different values when called with the same input
Let’s model a bank account that has a balance of $100; each time we do the same thing
withdraw(25)
, but we get different return values of money left.
Persistent Local State Using Environments
- All calls to the same function have the same parent
- We can get some techniques to make changes to the parent frame in a local frame
Reminder: local assignment
Assignment binds name(s) to value(s) in the first frame of the current environment.
Execution rule for assignment statements:
- Evaluate all expressions right of =, from left to right
- Bind the names on the left to the resulting values in the current frame
Non-local assignment & Persistent local state
def make_withdraw(balance):
"""Return a withdraw function with a starting balance."""
def withdraw(amount):
nonlocal balance #Declare the name "balance" nonlocal at the top of the body of the function in which it is re-assigned
if amount > balance:
return 'Insufficient funds'
balance -= amount #Re-bind balance in the first non-local frame in which it was bound previously
return balance
return withdraw
Mutable Functions II
The effect of nonlocal statements
nonlocal <name>, <name>, ...
- Effect: Future assignments to that name change its pre-existing binding in the first non-local frame of the current environment(an “enclosing scope”) in which that name is bound.
From the Python 3 language reference:
Names listed in a nonlocal statement must refer to pre-existing bindings in an enclosing scope.
Names listed in a nonlocal statement must not collide with pre-existing bindings in the local scope(current frame).
The many meanings of assignment statements
x = 2
Status | Effect |
---|---|
No nonlocal statement and “x” is not bound locally | Create a new binding from name “x” to object 2 in the first frame of the current environment |
No nonlocal statement and “x” is bound locally | Re-bind name “x” to object 2 in the first frame of the current environment |
nonlocal x and “x” is bound in a non-local frame | Re-bind “x” to 2 in the first non-local frame of the current environment in which “x” is bound |
nonlocal x and “x” is not bound in a non-local frame | SyntaxError: no binding for nonlocal ‘x’ found |
nonlocal x and “x” is bound in a non-local frame and “x” also bound locally | SyntaxError: name ‘x’ is parameter and nonlocal |
Python particulars
Python pre-computes which frame contains each name before executing the body of a function.
Within the body of a function, all instances of a name must refer to the same frame.
Mutable Values & Persistent Local State
Mutable values can be changed without a nonlocal statement.
def make_withsraw_list(balance):
b = [balance] #Name bound outside of withdraw def
def withdraw(amount):
if amount > b[0]:
return 'Insufficient funds'
b[0] = b[0] - amount #Element assignment of a list
return b[0]
return withdraw
Multiple Mutable Functions
Referential transparency, Lost
- Expressions are referentially transparent if substituing an expression with its value does not change the meaning of a program.
mul(add(x, mul(4, 6)), add(3, 5))
mul(add(2, 24), add(3, 5))
mul(26, add(3, 5))
#The expressions above are actually the same.
- Mutation operations violate the condition of referential transparency because they do more than just return a value; they change the environment.
Summary
- Nonlocal allows you to modify a binding in a parent frame, instead of just looking it up.
- Don’t need a nonlocal statement to mutate a value.
- A variable declared nonlocal must:
- Exist in a parent frame(other than the global frame)
- Not exist in the current frame
Program Performance
Measuring Performance
- Different functions run in different amounts of time
- Different implementations of the same program can also run in different amounts of time
- How do we measure this?
- Amount of time taken to run once
- Average time taken to run
- Average across a bunch of different computers
- Number of operations
Improving number of operations
- Getting better performance usually requires clever thought
- Some tools can be used to speed up programs(Ex: memoization, up next)
- Other times, need to have a different approach or incorporate some insight
Counting
How to count calls
Can’t always rely onn having a program to count calls
- For an iteration function: step through the program, and identify how many times you need to go through the loop before exiting
- For a recursive function: draw out an environment diagram/call tree
After you do this for a few example inputs, you can find patterns to estimate the number of steps for other inputs.
“Patterns” of Growth
- There are common patterns for how functions grow
- Because these patterns often aren’t linear, you often can’t use just one input to compare programs
- Instead, you have to identify the overall pattern
- Constant Growth
- Linear Growth
- Logarithmic Growth
- Exponential Growth