= 2
x = 3
y = x + y
x
for i in range(6):
= x + i
x
print('x =', x)
x = 20
The main goal of this module is to step back and review core concepts in programming: loops, conditionals, lists, functions.
Consider:
Variables have four aspects to them:
x
or immensely_pleased
above.
Let’s examine what happens when each line in the above program executes:
When the first line executes:
After the second line executes:
After the third line:
After the fourth:
After the fifth:
Then, one of the variables does have its value replaced:
Finally, one of the elements in the list gets replaced:
Now, there is a somewhat highly technical point to be made: - The “box” is what we’re using to conceptualize what a variable is, and how it behaves when we change it. - Generally, this is how you should think of it. - However, some books will present certain kinds of variables differently, with more detail. - Underneath the hood, in fact, string and list variables are a bit different.
So, for the sake of completeness, we’ll just point out this more detailed version:
Here:
s[2]
(3rd char in string).Let’s start with a simple example:
Let’s look at this in steps:
First, start by noticing the chunks of code:
Next, walk through the execution of the first chunk, and notice that the value of x
is what’s used later:
x
and y
, then do so.
x = x + y
and say to yourself: “First, let’s look at the right side, and the values of x
and y
now”.x
and y
.x
(5, in this case) that replaces what was there (which was 2).Then get down to the iterative level and execute iteration by iteration:
i x
Before loop starts: 5
After first iteration 0 5
After i is 1 1 6
After i is 2 2 8
After i is 3 3 11
After i is 4 4 15
After i is 5 5 20
Finally, there’s the print
statement:
Next, let’s look at reading a complex conditional:
age = 17
maximum_feasible_age = 120
voting_age = 18
berlin_age = 16
toronto_age = 19
washington_age = 21
if age >= maximum_feasible_age or age < 0:
print('That age is not realistic.')
else:
if age >= voting_age:
print('This person can vote in the USA.')
if age > toronto_age:
print('This person can buy a beer in Toronto.')
if age > washington_age:
print('This person can buy a beer in Washington, DC.')
elif age >= berlin_age:
print('This person can buy a bottle of beer in Berlin.')
else:
print('This person is a minor.')
We’ll do this in steps, working from the “outside going in”:
The conditions depend on the variables above.
The boolean expression maximum_feasible_age or age < 0
fails, so we move on to the else
, which executes.
Then, working inwards, we examine and see that the else
statement’s code block is itself a big conditional:
if
condition fails (age >= voting_age
is False
) and so execution proceeds into the elif
block:elif
statement is True
, and so its body (a print
statement) executesThis example illustrates how critical it is to make sure the indentation is correct. For example, consider this variation:
Moving the indentation inward changes the logical execution of the series of conditionals.
Consider this example:
Let’s look at the execution step by step:
print
statement, followed by the first function call (or function invocation). At this point, execution enters the function:Consider this program:
def print_two_xs():
print('xx')
def print_three_ys():
print('yyy')
def print_more():
print_two_xs()
print_three_ys()
print('z')
print('start')
for i in range(8):
if i % 3 == 0:
print_two_xs()
elif i % 3 == 1:
print_three_ys()
else:
print_two_xs()
print_three_ys()
print_more()
print('end')
Here’s an example:
def print_stuff(n):
print('n =', n)
n = 2*n
print('twice n =', n)
n = n // 4
print('half n =', n)
m = 4
print(m)
print_stuff(m)
print(m)
n
gets its value from the argument variable://
is the integer division operator. Later in the function, the parameter variable gets its value changed:It’s important to realize that we could have named the argument variable n
as well:
Any code inside the function that uses or modifies n
affects only the parameter variable n
(the n
inside the function and not the n
outside the function).
Let’s emphasize one more thing by looking at:
def print_stuff(n):
print('n =', n)
n = 2*n
print('twice n =', n)
n = n // 4
print('half n =', n)
print('m =', m) # Trying to access m
m = 4
print(m)
print_stuff(m)
print(m)
print('n =', n) # Trying to access n
You will notice that m
is accessible in the function whereas the parameter variable n
is not accessible outside. This is a topic (called scope) that we’ll address in Unit 2.
Let’s look at an example:
incr()
function occurs: j = incr(i)
i
is copied into parameter variable n
:m
becomes 6, which is returned when the return
statement executes:j
has the value 6.k = incr( incr( incr(j) ) )
which calls to incr()
with argument 6, returning 7, which results in k = incr( incr(7) )
k = incr( incr( 7 ) )
calls incr()
with argument 7, which returns 8, which results in k = incr( 8 )
k = incr( 8 )
similarly results in k = 9
return
statement can have expressions. Which means we can shorten incr()
to:return
statement itself:This is a shorter way of writing
Consider a function with multiple returns:
def strange(n):
print('start-of-strange')
if n < 0:
return 0
elif n == 0:
return 1
else:
s = 1
for k in range(n+1):
s = s + k
return s
print('end-of-strange')
print(strange(-3))
print(strange(0))
print(strange(3))
Note: In the first call to the function strange()
, the parameter n
will have the value -3. - In this case, we see ‘start-of-strange’ printed. The if
condition is true, which means the first return
executes:
def strange(n):
print('start-of-strange')
if n < 0:
return 0 # function returns here
elif n == 0:
return 1
else:
s = 1
for k in range(n+1):
s = s + k
return s
print('end-of-strange')
Consider
def add_one(n):
n = n + 1
print('incr: n=', n)
def list_add_one(A):
for k in range(0, len(A)):
A[k] = A[k] + 1
print('list_incr: A=', A)
p = 3
add_one(p)
print(p)
B = [1, 2, 3]
list_add_one(B)
print(B)
add_one()
function has no effect on the variable p
, since the value in p
gets copied into n
. The parameter variable n
does get 1 added (as the print
in add_one()
confirms.print(B)
shows that the elements of B
have 1 added to each of them.B
’s are copied into parameter variable A
, then the variable A
has the special token.And another example for practice:
def within1(x, y):
# Write your code here to return True or False
# Return True if the difference between x and y is 1 or less
def first_diff(A, B):
k = 0
while (k < len(A)) and (within1(A[k], B[k])):
k = k + 1
if k == len(A):
return -1
else:
return k
X = [1, 2, 3, 4]
Y = [2, 2, 3, 3]
Z = [1, 1, 1, 4]
print(first_diff(X,Y)) # Should print -1
print(first_diff(X,Z)) # Should print 2
Functions are very useful for four different reasons:
Code written in a function can be re-used. For example, compare:
X = [1, 3, 5, 7]
total = 0
for k in X:
total += k
avg = total / len(X)
print(avg)
Y = [2, 4, 6, 8, 10]
total = 0
for k in Y:
total += k
avg = total / len(Y)
print(avg)
with
def mean(A):
total = 0
for k in A:
total += k
return total / len(A)
X = [1, 3, 5, 7]
print(mean(X))
Y = [2, 4, 6, 8, 10]
print(mean(Y))
The second reason is, as this example shows:
(You can imagine what the functions incr()
and diff()
do.) Another example showing compactness with functions:
A long program broken up into functions will make the program more readable and therefore more easily understood. The biggest reason, perhaps, is that it has become one of two important ways by which multiple programmers use each others’ code.
As an example, you have used functions in drawtool
and Python functions like math.random()
.
How do you know when to create functions vs. writing long code? There are no rules. The judgement comes with practice. Generally, tiny computations like increment don’t need functions. Any significant computation that is likely to be re-used should probably be in a function. Also, use functions when when doing so improves readability.
Full credit is 100 pts. There is no extra credit.