= "hello"
s 'h')
s.startswith('b') s.startswith(
True
False
Conditionals
if
-elif
-else
statements in PythonLet’s investigate a new string method called startswith
. We would guess that method will indicate if the string starts with the supplied parameter:
What are True
and False
. They are not strings because they’re not enclosed in quotes, nor are they integers or floating points numbers. They are bool
s, which is short for Boolean. Booleans are a type that can only take on two values True
and False
. Booleans and Boolean logic are a key programming tool, particularly for decision-making (i.e., do we want to do a particular operation or not).
All Boolean operations (i.e., computations with boolean inputs and a boolean output) can be implemented with a combination of the basic boolean operations AND, OR and NOT. Or in Python and
, or
, and not
. The first two are binary operations, i.e. have two inputs, the last is unary, i.e. has a single input.
Boolean operators are often expressed as truth tables, e.g. we would express the truth tables for a and b
and a or b
as:
a | b | or |
and |
---|---|---|---|
True | True | True | True |
True | False | True | False |
False | True | True | False |
False | False | False | False |
Much like arithmetic operators, boolean operators have precedence: not
, then and
, then or
.
We previously saw string methods that return
bool
. Another common way to generate bool
s is with relational operators applied to numerical (and other) inputs, e.g.
<, >, <=, >=, ==, !=
These mostly implement familiar relationships like greater than (\(\gt\)) or greater than or equal (\(\ge\)). But recall that in Python (unlike math), =
is assignment. Thus equality is implemented with ==
(and !=
implementing “not equals”).
One of the key uses for bool
s is making decisions. So far none of our programs have made any choices. Being able to do so is very powerful. The general pattern for conditional statements in Python:
If A evaluates to True
which statements will be executed? Statements 1,2,7,8. What if A evaluates to False
? If B evaluates to True
, statements 3,4,7,8. If neither A or B evaluate to True
, then statements 5,6,7,8 will execute.
Only one of the if
, elif
or else
is “selected” and its body executed, even if multiple of the boolean expressions would have evaluated to true, and that selection occurs in order (i.e., the first “true” branch is selected).
Note elif
and else
are optional and no branch of the conditional needs to be selected. Multiple elif
s are permitted.
Some other examples:
A note about coding “best practices”. Can we write is_odd
more concisely? Yes. Almost anytime we are return
ing a boolean from a conditional statement we can do so more concisely, e.g.,
Why is this better style? Recall that good coding style is often about minimizing the cognitive burden of reading code. When return
ing boolean values from within in if
-else
statement we first need understand the condition expression and then map it to separate, possibly different return
values. That is additional cognitive burden compared to just needing to understand the relational expression.
With our knowledge of functions, loops, lists and conditionals, we have all the pieces we need to program our first game! Together, we’ll develop our own turtle
-based version of Snake:
Download the template and save it to your cs146
folder. The game currently sets up the “callbacks” for when arrow keys are pressed, which allow us to control the snake. The gameplay loop is also already set up, however, there are some pieces missing in the callbacks and parts of the game that check if the snake eats a randomly-placed piece of food (which is stamp
ed as a turtle).
The pieces we need to complete are labelled with TODO
comments in the code.
key_left
, key_right
, key_up
and key_down
functions
def key_left():
"""
Callback for when the left arrow key is pressed.
"""
heading = turtle.heading()
if heading == 180:
return # already going left
elif heading == 270:
turtle.right(90)
return
turtle.left(90)
def key_right():
"""
Callback for when the right arrow key is pressed.
"""
heading = turtle.heading()
if heading == 0:
return # already going right
elif heading == 270:
turtle.left(90)
return
turtle.right(90)
def key_up():
"""
Callback for when the up arrow key is pressed.
"""
heading = turtle.heading()
if heading == 90:
return # already going up
elif heading == 0:
turtle.left(90)
return
turtle.right(90)
def key_down():
"""
Callback for when the down arrow key is pressed.
"""
heading = turtle.heading()
if heading == 270:
return # already going down
elif heading == 0:
turtle.right(90)
return
turtle.left(90)
snake_ate_food
function
def snake_ate_food(food):
"""
Determines if the snake is at the food using the current turtle position.
Returns:
True if distance from the current turtle position to food is less than SIZE.
"""
p = turtle.position()
px = p[0]
py = p[1]
fx = food[0]
fy = food[1]
distance = math.sqrt((px - fx)**2 + (py - fy)**2)
return distance < SIZE
One of the ways to calculate π is via Monte Carlo sampling. Consider a quarter circle inscribed inside a square of side 1 (i.e. with an area of 1). The area of the quarter circle is \(\frac{\pi 1^2}{4}\) and thus ratio of that area to that of the square is π/4. If we randomly select points inside the square, approximately a π/4 fraction of those points should be inside the quarter circle (imagine randomly throwing darts at square with an inscribed quarter circle). By calculating the ratio of randomly sampled points inside the circle to the total number of sample, we can estimate the value of π.
Let’s implement a function calculate_pi
with a single parameter, the number of sampled points. What should we expect as we increase the number of samples? We will get closer and closer (probabilistically) to the actual value of pi. For example:
4.0
3.16
3.152
3.1664
3.13752
import math, random
def calculate_pi(num_samples):
"""
Approximate pi via Monte Carlo sampling
Args:
num_samples: number of Monte Carlo samples
Returns:
Approximate value of pi
"""
in_circle = 0
for i in range(num_samples):
# Generate random "dart" inside unit square
x = random.uniform(0, 1)
y = random.uniform(0, 1)
# Determine distance to origin
dist = math.sqrt(x*x + y*y)
# Count number of darts inside the circle
if dist <= 1:
in_circle += 1 # equivalent to in_circle = in_circle + 1
# Calculate pi based on ratio of "darts" inside circle vs total samples
return (4 * in_circle) / num_samples