Back to CS 106A Homepage
Written by Brahm Capoor, Juliette Woodrow and Parth Sarin
January 20th, 2020
def rotate_image_left(image):
out = SimpleImage.blank(image.height, image.width)
for y in range(image.height):
for x in range(image.width):
source_pixel = image.get_pixel(x, y)
dest_pixel = image.get_pixel(y, image.width - 1 - x)
dest_pixel.red = source_pixel.red
dest_pixel.green = source_pixel.green
dest_pixel.blue = source_pixel.blue
return out
def flip_vertical(image):
out = SimpleImage.blank(image.height, image.width)
for y in range(image.height):
for x in range(image.width):
source_pixel = image.get_pixel(x, y)
dest_pixel = image.get_pixel(x, image.height - 1 - y)
dest_pixel.red = source_pixel.red
dest_pixel.green = source_pixel.green
dest_pixel.blue = source_pixel.blue
return out
def draw_concentric_ovals(canvas, x,y, width, height, num_circles, padding):
x_val = x
y_val = y
canvas.draw_oval(x_val, y_val, width, height)
for i in range(num_circles-1):
x_val += padding
y_val += padding
width = width - 2*padding
height = height - 2*padding
canvas.draw_oval(x_val, y_val, width, height)
def draw_diamond_design(canvas, n, width, height):
center_x = width // 2
center_y = height // 2
x_factor = center_x // n
y_factor = center_y // n
# Top right
for i in range(n):
x = center_x + x_factor * i
y = center_y - y_factor * (n - i)
canvas.draw_line(x, center_y, center_x, y)
# Bottom right
for i in range(n):
x = center_x + x_factor * i
y = center_y + y_factor * (n - i)
canvas.draw_line(x, center_y, center_x, y)
# Bottom left
for i in range(n):
x = center_x - x_factor * i
y = center_y + y_factor * (n - i)
canvas.draw_line(x, center_y, center_x, y)
# Top left
for i in range(n):
x = center_x - x_factor * i
y = center_y - y_factor * (n - i)
canvas.draw_line(x, center_y, center_x, y)
def draw_pyramid(canvas, width, height, num_rows, block_width, block_height):
"""
Draw a pyramid, with num_rows rows, and the specified block
dimensions.
Arguments:
canvas -- The canvas to draw on.
width -- The width of the canvas.
height -- The height of the canvas.
num_rows -- The number of rows in the pyamid.
block_width -- The width of each block in the pyramid.
block_height -- The height of each block in the pyramid.
"""
for row in range(num_rows):
base_x = row * block_width // 2
y = height - (row + 1) * block_height
for block in range(num_rows - row):
x = base_x + block_width * block
canvas.draw_rect(x, y, block_width, block_height)
def in_range(n, low, high):
if n >= low and n <= high:
return True
return False
def is_even(n):
if n % 2 == 0:
return True
return False
def is_prime(n):
for possible_factor in range(2, n):
# we skip 0 and 1, since they're not the factors we're interested in
if n % possible_factor == 0:
# we've found a factor of n, so n isn't prime and we can return False
return False # ends the function
# if the loop ends, it means we didn't find any factors
# and so we just return True
return True
def only_one_even(num1, num2):
if num1 % 2 == 0 and num2 % 2 == 1:
return True
if num1 % 2 == 1 and num2 % 2 == 0:
return True
return False
def only_one_even(num1, num2):
if (num1 % 2 == 0 and num2 % 2 == 1) or (num1 % 2 == 1 and num2 % 2 == 0):
return True
return False
def is_power_of_two(n):
if n < 1:
# powers of 2 are equal to 1 or greater
return False
while n > 1:
if n % 2 == 1:
# n is odd, so can't be divided by 2. Therefore it's not a power of 2
return False
n /= 2 # divide n by 2, and continue checking
# we got down to 1, so n is a power of 2!
return True
def convert_to_int(s):
result = 0
for ch in s:
result *= 10
result += int(ch)
return result
def remove_all_occurrences(s, to_remove):
result = ""
for ch in s:
if ch != to_remove:
result += ch
return result
def is_palindrome(s):
for i in range(len(s)):
if s[i] != s[len(s) - 1 - i]:
return False
return True
def add_commas_to_numeric_string(digits):
"""
Solution 1
"""
result = ""
num_digits = 0
for char_index in range(len(digits)):
result = digits[len(digits) - char_index - 1] + result
num_digits += 1
if num_digits % 3 == 0 and char_index > 0 and \
num_digits < len(digits):
"""
We need to check that
a) we've added a multiple of 3 number of digits
b) we've added at least one digit
c) there are more digits to add
"""
result="," + result
return result
def add_commas_to_numeric_string(s):
"""
Alternate Solution
>>> add_commas('1234567890')
'1,234,567,890'
>>> add_commas('12345')
'12,345'
>>> add_commas('1234')
'1,234'
>>> add_commas('123')
'123'
>>> add_commas('12')
'12'
>>> add_commas('1')
'1'
"""
result = ''
for i in range(len(s)):
# Append the digits left-right as usual.
# Notice if multiple-of-3 to_do after the digit just added.
result += s[i]
to_do = len(s) - (i + 1)
if to_do > 0 and to_do % 3 == 0:
result += ','
return result
def exclaim(msg, end, n):
"""
Prints out the message with an exclamatory
ending which is printed n number of times.
Arguments:
msg -- The message to print out.
end -- The exclamation to add to the end of the message.
n -- The number of times to print the exclamation.
"""
output = msg
for i in range(n):
output += end
print(output)
When writing doctests, your test cases should cover a wide enough range of possible inputs that you feel comfortable that your function behaves as expected. Part of this is including what you'd consider to be 'normal' input: that is, input to the function that is easy to reason about and which are easy to program for. However, equally important is testing that your function gracefully handles the more annoying cases that are slightly harder to program for. In programming, these are called 'edge cases' and refer to inputs for which your function might need special logic. As a general rule, try to find test cases that could possibly break your function, rather than those which confirm what you know to be true about it already.
A second consideration is that doctests can help document your function: someone reading them can start to understand what your function does without having to reflect on the code. Thus, it's usually a good idea to have inputs that are relatively simple to reason about: small numbers and short strings, for example. In addition to comments about your function, doctests are a great way of specifying behaviour.
There's no hard and fast rule about this (unless we specify one in an assignment!). Rather, add doctests until you're confident in your coverage of possible inputs to your function, but don't add doctests for the sake of doctests.
There is a very long list of possible doctests one could add, but here are a few ideas for inputs you might want to try:
in_range
: cases where n
is and is not in the range, as well as
cases in which the range is invalid (i.e high
is less than low
),
or has the same start and end point.
is_power_of_two
: negative input, 0, powers of 2 and non-powers of 2.
convert_to_int
: "0"
and some numbers of different lengths.
remove_all_occurrences
: A string which does not contain to_remove
,
a string that is entirely to_remove
, and some more conventional cases.