Session 2 Recap

 Problem Solving with Python Recap challenges 2: from the specific to the general Contents 1 Drawing a square in Python ................................................................................................... 1 2 Drawing any regular polygon ................................................................................................ 2 3 Drawing spirographs ................................................................................................................. 6 4 Asking users for program parameters ............................................................................... 8 5 Switching between colours when drawing a spirograph .......................................... 9 1 Drawing a square in Python The following Python program solves the specific problem of drawing a square with side length 100. from turtle import *
forward(100)
right(90)
forward(100)
right(90)
forward(100)
right(90)
forward(100)
right(90)
If we want to draw a square with side length 50, we could make 4 changes to the above program as follows: from turtle import *
forward(50)
right(90)
forward(50)
right(90)
forward(50)
right(90)
forward(50)
right(90)
We can continue to produce different versions of essentially the same program with different side lengths to produce different sized squares. 1 Alternatively, we can use variables to abstract away from the specific values used for the length of a side and the interior angle of a square to produce the following general solution to drawing a square: from turtle import *
side_length = 100
# declare a variable called
# side_length and give it the
# value 100
angle = 90
# declare a variable called
# angle and give it the
# value 90
forward(side_length)
right(angle)
forward(side_length)
right(angle)
forward(side_length)
right(angle)
forward(side_length)
right(angle)
Instead of using 50 or 100 or some other fixed value as the parameter to the forward function, we use the variable side_length. Instead of using 90 as the parameter to the right function, we use the variable angle. This is just like using cell names in formulas in Excel. We can change the value assigned to the variable side_length to produce squares of different sizes. Try different values for the angle and the above code will produce 4 sides of any regular polygon. 2 Drawing any regular polygon At the end of section 1 we have a general solution to the problem of drawing a square. We also have the basis of a solution to drawing any regular polygon. Changing the value for the angle produces 4 sides of a different polygon. For example, changing the program to: from turtle import *
side_length = 100
angle = 72
forward(side_length)
right(angle)
forward(side_length)
right(angle)
forward(side_length)
right(angle)
forward(side_length)
right(angle)
2 produces 4 sides of a pentagon. We can make this clearer by introducing another variable for the number of sides and calculate the interior angle from the number of sides as follows: from turtle import *
side_length = 100
sides = 5
angle = 360/sides
forward(side_length)
right(angle)
forward(side_length)
right(angle)
forward(side_length)
right(angle)
forward(side_length)
right(angle)
This produces the following shape: There is clearly a bug if we are trying to draw a pentagon. The 5th side is not drawn. We could change the value of sides to 3, as follows: from turtle import *
side_length = 100
sides = 3
angle = 360/sides
forward(side_length)
right(angle)
forward(side_length)
right(angle)
forward(side_length)
right(angle)
forward(side_length)
right(angle)
and produce the following shape: 3 This has a more subtle bug. The first (horizontal) side of the triangle is drawn twice. We can remove the bugs in our programs by duplicating or deleting forward/right pairs of lines to produce a shape with the correct number of sides. For example, 3 forward/right pairs of lines for a triangle (when sides is 3), 4 forward/right pairs of lines for a square (when sides is 4) and the following program for a pentagon (when sides is 5): from turtle import *
side_length = 100
sides = 5
angle = 360/sides
forward(side_length)
right(angle)
forward(side_length)
right(angle)
forward(side_length)
right(angle)
forward(side_length)
right(angle)
forward(side_length)
right(angle)
The problems with this approach are: 1. We have to produce a new program for each different polygon we want to draw (one program for a triangle, another for a square etc). 2. It is error-­‐prone. We have to make sure that we have the same number of pairs of forward/right lines of code as the number of sides we specify. If the value for sides is less than the number of forward/right pairs, the program will not draw enough sides to complete the polygon. If the value of sides is greater than the number of forward/right pairs, the program will overwrite one or more sides of the polygon. That is, the duplication of forward/right pairs of the lines not only causes extra work it can also introduce bugs. If we imagine the program was totalling bank balances instead of drawing shapes, we would have some very unhappy customers! 4 The solution is to use a loop to repeat the relevant lines of code for us, as follows: from turtle import *
side_length = 100
sides = 5
angle = 360/sides
for side in range(sides):
forward(side_length)
right(angle)
Now we have a general solution to drawing a regular polygon. The final three lines of code are written entirely in terms of the variables: side_length, sides, and angle, which is calculated from sides. They represent an abstraction of how to draw a regular polygon. We only have to change the value of side_length and sides to draw polygons of different sizes and number of sides, respectively. This is much less error prone and a completely general solution to the problem of drawing a regular polygon. Note: the indentation beneath the for statement is part of Python syntax and means that the forward and right statements are repeated sides times. The equivalent program in Scratch is: from turtle import *
side_length = 100
sides = 5
angle = 360/sides
for side in range(sides):
forward(side_length)
right(angle)
5 3 Drawing spirographs We can create spirographs by repeatedly drawing a polygon while rotating through 360 degrees. For example, the following program: from turtle import *
side_length = 100
sides = 3
angle = 360/sides
polygons = 5
rotation = 360/polygons
# polygon 1
for side in range(sides):
forward(side_length)
right(angle)
turn(rotation)
# polygon 2
for side in range(sides):
forward(side_length)
right(angle)
turn(rotation)
# polygon 3
for side in range(sides):
forward(side_length)
right(angle)
turn(rotation)
# polygon 4
for side in range(sides):
forward(side_length)
right(angle)
turn(rotation)
# polygon 5
for side in range(sides):
forward(side_length)
right(angle)
turn(rotation)
produces the following shape: 6 As with drawing a polygon, we have a specific solution to drawing a spirograph of 5 triangles. We can use another loop to generalise this program to draw a spirograph with any number of polygons. The following Scratch and Python programs show the generalisation using two new variables: •
•
polygons for the number of polygons to draw,and rotation for the angle to rotate through after each polygon is drawn, which is calculated from the number of polygons. In the following examples, sides is set to 3 and polygons is set to 5 to reproduce the preceding shape. Changing the values of these variables produces different spirographs. from turtle import *
side_length = 100
sides = 3
angle = 360/sides
polygons = 5
rotation = 360/polygons
speed(0)
for polygon in range(polygons):
for side in range(sides):
forward(side_length)
right(angle)
right(rotation)
The outer and inner repeat loops in Scratch and the outer and inner for loops in Python (in bold) are abstractions of how to draw a spirograph written in terms of the variables: side_length, sides, angle, polygons and rotation. 7 4 Asking users for program parameters Now that we have the following general program for drawing spirographs: from turtle import *
side_length = 100
sides = 3
angle = 360/sides
polygons = 5
rotation = 360/polygons
speed(0)
for polygon in range(polygons):
for side in range(sides):
forward(side_length)
right(angle)
right(rotation)
we can ask a user for the values for the variables side_length, sides and polygons. That is, we can use user input to paramaterise our program, or set the conditions for our program. We use Python's input function to do this. The input function prints a user prompt to the screen and waits for the user to type something. Whatever the user types becomes the result of the input function. If we want to treat the result as an integer value, we convert it to an integer using the int function. For example, the following code: answer = input('Enter length of polygon sides: ')
side_length = int(answer)
Prints "Enter length of polygon sides: " to the screen and waits for user input. Whatever the user types is stored in the variable answer (as a string). Then, answer is converted an integer using the int function and the result of the conversion is stored in the variable side_length. So we can modify the preceding program as follows: from turtle import *
answer = input('Enter length of polygon sides: ')
side_length = int(answer)
answer = input('Enter number of sides of polygon: ')
sides = int(answer)
angle = 360/sides
answer = input('Enter number of polygons: ')
polygons = int(answer)
rotation = 360/polygons
speed(0)
for polygon in range(polygons):
for side in range(sides):
forward(side_length)
right(angle)
right(rotation)
8 to allow a user to completely specify how to draw a spirograph. The equivalent program in Scratch is: Note: Scratch uses the context in which an answer is used to decide whether it should be treated as a string or an integer (number) but we have to tell Python (using the int function). We now have programs in which the only fixed value is 360 (used to calculate angles). The programs are completely general and abstract away from the actual values required to draw a specific polygon or spirograph. 5 Switching between colours when drawing a spirograph We will now modify our program so that the first polygon in a spirograph is red, then the second polygon is green, then the third polygon is blue, then the fourth is red again and so on. 9 The following figure shows the expected result for a spirograph of five triangles: We can do this by using a variable called colour_index to represent the colour to use for the current polygon (0 for red, 1 for green, 2 for blue) and changing the value of the variable after each polygon has been drawn. The program outline is: repeat until drawn all polygons:
if colour_index is 0:
set pen colour to red
if colour_index is 1:
set pen colour to green
if colour_index is 2:
set pen_colour to blue
draw polygon
colour_index = colour_index + 1
if colour_index is 3:
colour_index = 0
That, is use red (0), then green (1), then blue (2) and when colour_index is 3 make sure we start at red (0) again for the next polygon. 10 The modified Python program is: from turtle import *
answer = input('Enter length of polygon sides: ')
side_length = int(answer)
answer = input('Enter number of sides of polygon: ')
sides = int(answer)
angle = 360/sides
answer = input('Enter number of polygons: ')
polygons = int(answer)
rotation = 360/polygons
colour_index = 0
speed(0)
for polygon in range(polygons):
if colour_index == 0:
color('red')
if colour_index == 1:
color('green')
if colour_index == 2:
color('blue')
for side in range(sides):
forward(side_length)
right(angle)
colour_index = colour_index + 1
if colour_index == 3:
colour_index = 0
right(rotation)
11 The corresponding Scratch program is: The preceding programs solve our problem for alternating between three colours. However, they will become increasingly complicated if we add more colours. For example, if we use the same structure to alternate between red, 12 green, blue and black we would need an additional if statement before drawing a polygon and would have to remember to reset colour_index back to 0 when it reached the value 4. For example: …
…
for polygon in range(polygons):
if colour_index == 0:
color('red')
if colour_index == 1:
color('green')
if colour_index == 2:
color('blue')
if colour_index == 3:
color('black')
for side in range(sides):
forward(side_length)
right(angle)
colour_index = colour_index + 1
if colour_index == 4:
colour_index = 0
right(rotation)
For each colour we add, we need a new if statement and need to keep track of a different value for colour_index to reset. This approach is complex and error prone. We can use a list to simplify the solution. 13