Today: if statement, ==
, function call, decomposition
go-green While Loop Example
Recall from last time, go-green demo:
> go-green
One Code - Many Cases - Generality
- There is one copy of your code algorithm
- There are many possible "cases" for the code
- Your code should work for all of them
- "Generality"
- See the Pop-Up menu by the Run button
- Try Run All option
For building your code, just run 1 case at a time
Run All is just a last check
While Loop - Zero Iterations
- It's possible for the body to run zero times
- If the test is False the very first time
- Each body run is called an "iteration"
- Try the width-1 case - think about the while-test
Aside: Bit Controls
- Can control how Bit runs
- Diff marks - red marks on world vs. desired output
- Speed
- End State - instead of animating, just show end state
If Statement - Logic
While loop is power. If-statement is control, controlling if lines are run or not
Problem statement: Move bit forward until blocked.
For each moved-to square, if the square is blue, change
it to green. (Demo: this code is complete.).
>
If Demo
For reference here is the code:
def if_demo(filename):
bit = Bit(filename)
while bit.front_clear():
bit.move()
if bit.get_color() == 'blue':
bit.paint('green')
If Statement Syntax
Syntax 4 parts (similar to "while"): if, boolean test-expression, colon, indented body lines
if test-expr:
body lines
If Statement Operation
- If statement evaluates the test expression, looking for
True
or False
- If test is True, runs the body top to bottom
- Otherwise skips the body, run continues after the body
- Basically an On/Off switch - runs the body lines or runs nothing
Look At Test Expression
Here are the key lines of code:
....
if bit.get_color() == 'blue':
bit.paint('green')
....
1. bit.get_color() Expression
- Recall: "expression" - code that runs and returns a value to use
-
bit.get_color()
expression
- returns the color value of the current square, one of:
'red' 'green' 'blue'
or None
if square is not painted
None
is the special Python value meaning "nothing"
It's handy in computer code to a formal "nothing" value for situations like this
Suppose bit is on a squared painted 'blue'. Here is diagram - visualizing that bit.get_color()
is called and it returns the value'blue'
to be used by the calling code.
2. ==
Compares Two Values - Boolean
- Very often used in an if-test
- The operator
==
compares two values for equality
(two equal signs next to each other)
Often use this in an if-test
- Returns True if two values are equal, False otherwise
- e.g.
if x == 6:
tests if x is 6
- Variant: not-equal comparison is written with an exclamation mark:
!=
- e.g.
if x != 6:
tests for any value but 6
- Style: if you want to express the "not-equal" idea,
!=
is the preferred way to do it
- Examples...
if bit.get_color() == 'red':
# run this if color is 'red'
if bit.get_color() == None:
# run this if square is not painted
if bit.get_color() != 'red':
# run this if square is anything but 'red'
# e.g. 'green' 'blue' or None
Decomposition - Program and Functions
"Divide and Conquer" - a classic strategy, works great with code
- "Decomposition" - just starting today
- Divide a program into functions
- One function for each sub-situation
- "Call" the right function when that situation appears
- Leverage - a bit magical
Write the function once
Then just call it when we need that situation solved
- This theme animates some later examples today
Functions - Wizard of EarthSea
In with Wizard of EarthSea novels by Ursula Le Guin .. each thing in the world has its true name.
A magician calls a thing's true name, invoking that thing's power. Function calls work like this - a function has a name, you invoke the function by its name, getting its power.
How To Call a Function
We've seen def
many times - defines a function. Note that
each function has a name and body lines of code, like this "go_west" function:
def go_west(bit):
bit.left()
bit.paint('blue')
...
1. Call by noun.verb
To "call" a function means to go run its code. One way to call a function in Python is the "object oriented" form, aka noun.verb. The bit functions look like that, e.g. bit.left()
, here "left" is the name of the function.
2. Call by name
The other way to call a function is just by using its name with parenthesis. For the above function named "go_west", code to call it looks like:
...
go_west(bit)
...
Both of these function-call styles are widely used in Python code.
What Does Function Call Do?
Say the program is running through some lines, which we'll call the "caller" lines. Then the run gets to the following function call line; what happens?
# caller lines
bit = Bit('5x5.world')
go_west(bit)
bit.left()
...
What this means simply is: (a) go run the lines inside the "go_west" function, suspend running here in the caller. (b) When go_west() is done, return to these caller lines and continue running with the next line. In other words, the program runs one function at a time, and function-call jumps over to run that function.
Fill World Blue Example
The whole program does this: bit starts at the upper left facing down. We want to fill the whole world with blue, like this
Program Before:
Program After:
>
Fill
1. - Decompose fill_row_blue() "helper" function
First we'll decompose out a fill_row_blue() function that just does 1 row. Work on that function first.
This is a "helper" function - solves a smaller sub-problem.
fill_row_blue() Before (pre)
fill_row_blue After (post):
Function Pre/Post Conditions
- Fitting multiple functions together
- Think about their pre/post conditions
- Pre - precondition of function
state of world before it runs
- Post - postcondition after it runs
state of world after it's finished
- Pre/Post make the "contract" of function - what it does
What it gets to assume when it is called
What it promises to provide
Pre/Post - Why Do I Care?
- We are building a program of multiple functions
This practice is huge productivity booster
- Need to make sure the functions mesh together
- Can we just call a function any old time?
- No - need to meet its pre conditions
- Suppose we want to call
a()
and then call b()
- what's required?
- First check a() pre
- Then check the a() post, make adjustments to fit the b() pre
Look at fill_row_blue() Function
- Pre: bit is at left edge facing south
- Post: the horizontal row is all blue, bit is back at the start location and direction
- Convention: it's not required, but a post condition where bit returns to its original location is simple, so we often do it that way.
def fill_row_blue(bit):
bit.left()
bit.paint('blue')
while bit.front_clear():
bit.move()
bit.paint('blue')
bit.right()
bit.right()
while bit.front_clear():
bit.move()
bit.left()
Run fill_row_blue()
- We have this function that "solves" one row
- With the Run button .. we can test it on its own
- See the pre/post conditions at work
- Now comes the magic step...
2. Write fill_world_blue()
- Now write the fill_world_blue() function to solve the whole world
- Make a drawing to guide thinking
- Mindful of pre/post conditions
fill_world_blue() Functions - A Great Deal!
- Key Idea: call fill_row_blue() to solve that sub-problem
- Like you're the boss - do what I say!
- Think about its pre/post conditions when calling
- Questions: how are you going to fill that first row? later rows?
- Sketch the state of the world to think about progress
- Try using "End State" checkbox to skip to end-state output
- Start with 2 rows, then generalize to do all the rows
More Bit Tests: bit.left_clear()
+ not
bit.left_clear()
- Returns True if square to left is clear, false otherwise
So if True, could turn left and move
- Also have
bit.right_clear()
- Putting
not
to the left of a test inverts it
if bit.left_clear():
# run here if left is clear
if not bit.left_clear():
# run here if left is blocked, aka not-clear
Decomposition Example / Exercise: Cover
Bit starts next to a block of solid squares (these are not "clear" for moving).
Bit goes around the 4 sides clockwise, painting everything green.
Cover Before (pre):
cover_row() After (post):
cover_square() After (post):
1. Run cover_side()
cover_row(bit): Move bit forward until the right side is clear.
Color every square green.
(demonstrates not left-clear test).
Run this code with case-1, see what it does. Study the code to see how it works, think about its pre/post (make a little drawing).
2. Work on cover_square()
cover_square(bit): Bit begins atop the upper left corner, facing right.
Paint all 4 sides green. End to the left of the original position,
facing up.
Start by calling cover_row() once. How can we solve the whole thing? How to address
the pre/post conditions of cover_side()? Key ideas:
- Call previously defined function to do work
- Must figure on its pre/post
- Trap: think about code inside cover_side()
- Correct: think only of its pre/post
- "Like A Boss" - just call the function, it jumps to your command!
Optional - Probably Not Getting This Far
Boolean Expressions: and or not
- Something like
bit.left_clear()
returns a "boolean" True/False
- These operators combine boolean values:
and or not
- xx and yy = both sides must be True, then it is True
- xx or yy = either side or both sides are True, it is True
- not xx = inverts True/False
Boolean Examples..
Q1: when is the following if-test True?
if bit.left_clear() and bit.right_clear():
# when does this happen?
Q2: when is the following if-test True? (just changing and to or)?
if bit.left_clear() or bit.right_clear():
# when does this happen?
Q3: when is the following if-test True?
if not bit.left_clear():
# when does this happen?
Answers..
A1: When either both left and right are clear
A2: When either left or right are clear (or both)
A3: When the left is blocked (i.e. lear_clear() returns False)
Put It All Together - Applied Boolean Code
>
Bit Logic1 Exercises
First 3 are demos - walk through them
Then last 3 are little exercises to try .. see how many we get through today
Infinite Loops
- Oddball case: infinite loop
- The body lines run, but make the test False
- The lines run but never finish
- May get error message: timed out - possible infinite loop
System notices code running for a long time, terminates it eventually
- Click the Bit "stop" button to stop the line hilighting
- Students: avoid doing this - it's hard on the Bit server!
Nick demo:
while bit.front_clear():
bit.left()
bit.right()
Here's a more subtle bug. What is the problem here?
while bit.front_clear():
bit.move
A: Easy typo to make - without parenthesis, the move function does not happen! Correct is bit.move()