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
nis and is not in the range, as well as cases in which the range is invalid (i.e
highis 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.
"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.