Today; style 1.0, top-down decomp digital images, RGB, pixels, "foreach" loop, expressions
Fn Call Recap Diagram
Here's a diagram showing the function-call sequence we played with in lecture-3, e.g. the stripes example.
The run starts in the caller and goes over to run the lines in the called function. When the called function is finished, the run resumes where it left off in the caller.
Bonus fact: the variables, such a "x" above, are separate and independent for each function, so x in caller is a completely separate variable from x in foo.
Pep Talk
Week-1 and week-2 have the maximum number of things to learn of the whole quarter. It's all a little weird looking, but you can pick it up. Do some reps. Later weeks we will slow down.
stripe_world() - Issue With Odd Rows
Detail I glossed over last time, see Case-7.
>
Stripes
What if there are an odd number of rows available in the loop? Solution: add if-logic for the second move within the loop. See this on
def stripe_world(filename):
bit = Bit(filename) # provided line, load bit from filename
# vvv
fill_row_color(bit, 'blue')
while bit.front_clear():
bit.move()
fill_row_color(bit, 'green')
if bit.front_clear(): # Need this if
bit.move()
fill_row_color(bit, 'blue')
Coding Style 1.0 - Tactics
xx link to py-style1.html
CS106A doe not just teach coding. It has always taught how to write clean code with good style.
Your section leader will talk with you about the correctness of code, but also pointers for good style.
All the code we show you will follow PEP8, so just picking up the style tactic that way is the easiest.
Python Style1 doc - We'll scan through this
Decomposition Strategy - Divide and Conquer
- Starting today .. more later
- Confronted with a giant code problem
- Divide into more manageable sub-problems
aka "helper" functions
- Solve them independently - "Decomposition"
- Mesh the functions together to solve the whole thing
- Divide and Conquer is a simple strategy
It's also how all CS is done, crucial
- Today: decompose out a helper function
e.g. stripe_world() function solves the whole thing, calling the stripe_row_color() function to do one row.
Big Picture - Browser Example
- Think about the code of a web browser
- PNG images, urls, HTML, inline video, network packets, SSL encryption, DOM tree, ...
- You cannot keep the code for all of that in your head
- It is millions of lines of code
- But you can imagine a function that solves one part of it
render_png_image()
check_ssl_keys()
- This is how CS works
- Decompose into Functions
- Work on each function separately
Aside: Delegation and Knowing Things
- Knowlege is Power, so they say
- Say I call a wash_dishes() function
- I want the post condition: dishes are cleaned and not broken
- Do I care if the hand drying is counter clockwise?
- Do I care if they stand here or there?
- Do I care which lights are on?
- In CS these are called the "details" of the computation
- We care about the post condition
- Not knowing the details is power
- See below when calling helper functions
Top-Down Decomposition Steps
- Top-Down Decomposition recipe:
- We have problem X to solve
- Thinking up the code for solve_x() function
- Q: what would be useful helper function here?
- Q: something I could call to solve a sub-part of the problem?
- What should be the pre and post conditions of the helper?
- e.g. say we think up a function called helper()
- 1. Write the code for solve_x(), calling helper()
- 2. Now go implement the code for helper() too
Hurdles Decomposition Example
>
Hurdles
- Recall: bit.left_clear(), bit.right_clear()
- True if empty space in that direction.
- Use with "not", e.g. while left blocked:
while not bit.left_clear():
bit.move()
Hurdles Outline
- Look at solve_hurdles() code
- Write solve_1_hurdle() imagining helpers
- Then write helpers
- Keep pre/post in mind as we go
solve_hurdles()
- solve_hurdles() function - the whole problem
- Its helper could be solve_1_hurdle()
pre: facing north start of hurdle
post: facing north start of next hurdle
def solve_hurdles(filename):
"""
Solve the sequence of hurdles.
Start facing north at the first hurdle.
Finish facing north at the end.
(provided)
"""
bit = Bit(filename)
while False and bit.front_clear(): # remove False to run
solve_1_hurdle(bit)
solve_1_hurdle()
- Now focus on writing solve_1_hurdle() - the core of this example
What are its pre/post conditions
- What helpers would be useful in here?
- There are 4 moves, 2 moves of one type, 2 moves of a second type
- Write a helper function for each move type
- helper a: go_wall_right() - go while wall to right
- helper b: go_until_blocked() - go until wall in front
- Running a hurdle looks somewhat like a, a, b, b
With some turn/moves in between
- Figure out pre/post for these
- Write solve_1_hurdle() code calling them
- Then need to go write the helpers
- Divide and conquer theme: isolate a sub problem, write code for just that part
solve_1_hurde() Helpers + Triple Quote
Here is the solve_1_hurdle() code - write this one first, just image that that the helpers exist. Think about pre/post for each.
At the top of the function is a description of what the function does within triple-quote marks - we'll start writing these from now on.
The description is just a summary of the pre/post in words.
Q: When calling a helper function, do you think about its internal details? No! Just call it, and know the post condition will get done. Not knowing is power!
def solve_1_hurdle(bit):
"""
Solve one hurdle, painting all squares green.
Start facing north at the hurdle's west edge.
End facing north at the start of the next hurdle.
"""
go_wall_right(bit)
bit.right()
bit.move()
go_wall_right(bit)
bit.right()
go_until_blocked(bit)
bit.left()
go_until_blocked(bit)
bit.left()
Two More Helpers
Now write two more helpers, keeping the pre/post in mind. Earlier claimed that pre/post help to mesh the various functions together, and that's crucial here.
def go_wall_right(bit):
"""
Move bit forward so long as there
is a wall to the right. Paint
all squares green. Leave bit with the
original facing.
"""
bit.paint('green')
while not bit.right_clear():
bit.move()
bit.paint('green')
def go_until_blocked(bit):
"""
Move bit forward until blocked.
Painting all squares green.
Leave bit with the original facing.
"""
bit.paint('green')
while bit.front_clear():
bit.move()
bit.paint('green')
Run vs. Helper Tests
When all the code is written, remove the False
from the solve_hurdles() and give it a Run. Previously we had separate testing for each helper which is ideal, and we will do that again in the future. In this case, we just run the whole thing and see if it works without the benefit of helper tests.
This sort of decomposition appears on HW1b - triple and checkerboard - decompose out helper functions as above to solve the whole thing.
Digital Images
- Originally the internet was made of text
- But perhaps images is where it really shines
- Digital images are made of small square "pixels"
- Each pixel shows a single color
- The color of a pixel is very often encoded as RGB
- pebbles.jpg
- Demo: Zoom in on image in Mac Preview (New From Clipboard feature - pro tip!)
RGB Color Scheme
- The red-green-blue scheme, RGB
- The color is defined by three numbers: red, green, blue
- Each number in the range 0..255
- (Why 255? I'll explain later)
- Each number represents a brightness of red/green/blue lights
- 0 = light is off
- 255 = light at maximum
- Can mix these 3 lights to make any color!
- Define any color by 3 numbers, 0..255
- rgb-explorer
- Nick's LED demo (this thing is a "MaxM" unit)
- https://thingm.com/products/blinkm-maxm
Image = Pixels, Coordinates, RGB
- Image is made of square pixels
- Pixels are in a x,y coordinate scheme
- Origin (0, 0) at the upper left (idiomatic CS)
- x = 0 is the left column, growing to right
- y = 0 is the top row, growing down
- Each pixel shows a single color
- Color encoded as 3 RGB numbers
- Image with 1 million pixels = 3 million numbers
# Load image into Python "image" variable
image = SimpleImage('flowers.jpg')
Best Loop Form: for
- "while" loop is great, the #2 most useful
- #1 most useful loop in Python: for
for x in collection:
# use x in here
x.do_something()
- Loop over any collection of things
- Runs the loop body once for each thing
- Called "foreach" in many computer languages
Image1 Code Examples
> Image1 Functions
Image For-Loop - red_channel()
def red_channel(filename):
"""
Creates an image for the given filename.
Changes the image as follows:
For every pixel, set green and blue values to 0
yielding the red channel.
Return the changed image.
"""
image = SimpleImage(filename)
for pixel in image:
pixel.green = 0
pixel.blue = 0
return image
- Create image variable from filename
- filename will be something like "flowers.jpg"
- "image" variable points to image object in memory
- Q: how many times does first line run? How many times the lines in the loop?
- for-loop
The loop runs once for each pixel in image
"pixel" var, refers to each pixel in turn
- "return image" - standard last line
- Returns modified image to caller line - more on this later
In this case the caller puts the image on screen
Update Variable: x = x + 1
- Update the value of a variable
- Variable is on both left and right of =
- This changes the variable in a relative way
- Code rule:
- 1. first "evaluate" right side expression, after the =
- 2. assign that value back into the variable
x = 6
x = x + 1
# what is x now?
image1-b. Make Image Darker
- Relative change of red/green/blue on each pixel
- Use "//" for division here, explained later
- Try dividing by bigger values, running code, seeing a new image
- Shorthand equivalent:
pixel.red //= 2
for pixel in image:
pixel.red = pixel.red // 2
pixel.green = pixel.green // 2
pixel.blue = pixel.blue // 2