This week in section, your goal is to practice and gain familiarity with the principles of parameters and return values, experiment with lists, and work on some interesting image problems using the SimpleImage library. We'll also explore some interesting applications of doctests in the debugging process.
Here is the starter code:
Parameters
Checking Ranges
Implement the following function which takes in 3 integers as parameters
def in_range(n, low, high)
"""
Returns True if n is between low and high, inclusive.
high is guaranteed to be greater than low.
"""
Next, complete the program by writing a main function which reads in three integers from the user and calls in_range to determine whether or not the second number is in between the first and third. Here are a few sample runs of the program (user input is italicized):
Enter first number: *42* Enter second number: *8* Enter third number: *50* 8 is not in between 42 and 50 Enter first number: *8* Enter second number: *42* Enter third number: *50* 42 is in between 8 and 50 Enter first number: *50* Enter second number: *42* Enter third number: *8* 42 is in between 50 and 8
def in_range(n, low, high):
"""
Returns True if n is between low and high, inclusive.
high is guaranteed to be greater than low.
"""
if n >= low and n <= high:
return True
# we don't need an else statement here because
# returning True would have ended the function
# early
return False
def in_range(n, low, high):
"""
A clever alternative solution that leverages the fact that
we're computing a boolean expression anyway
"""
return n >= low and n <= high
def main():
num_1 = int(input("Enter first number: "))
num_2 = int(input("Enter second number: "))
num_3 = int(input("Enter third number: "))
if in_range(num_2, num_1, num_3) or in_range(num_2, num_3, num_1):
print(str(num_2) + " is in between " +
str(num_1) + " and " + str(num_3))
else:
print(str(num_2) + " is not in between " +
str(num_1) + " and " + str(num_3))
if __name__ == "__main__":
main()
FizzBuzz
In the game Fizz Buzz, players take turns counting up from one. If a player’s turn lands on a number that’s divisible by 3, she should say “fizz” instead of the number, and if it lands on a number that’s divisible by 5, she should say “buzz” instead of the number. If the number is both a multiple of 3 and of 5, she should say "fizzbuzz" instead of the number. A spectator sport, it is not.
What it is, however, is an interesting problem in control flow and parameter usage. Write a function called fizzbuzz which accepts as a parameter an integer called n. The function should count up until and including n, fizzing and buzzing the correct numbers along the way. Once it's done, the function should return how many numbers were fizzed or buzzed along the way.
Next, complete your program by writing a main function that reads in an integer from the user and plays fizzbuzz until it counts to the number then prints out the count of the numbers that were fizzed, buzzed, or fizzbuzzed. Here's a sample run of the program (user input is italicized):
Number to count to: *17* 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 Fizzbuzz 16 17 7 numbers were fizzed, buzzed, or fizzbuzzed
def fizzbuzz(n):
count = 0
for i in range(1, n + 1):
if i % 15 == 0:
count += 1
print("Fizzbuzz")
elif i % 3 == 0:
count += 1
print("Fizz")
elif i % 5 == 0:
count += 1
print("Buzz")
else:
print(i)
return count
def main():
num = int(input("Number to count to: "))
count = fizzbuzz(num)
print(str(count) + " numbers were fizzed or buzzed")
if __name__ == "__main__":
main()
Lists
Interesting List
Implement the following function:
def make_interesting_list(num_list):
"""
Accepts a list of integers and returns a new list based on the given list.
The new list should have each of the values in num_list as follows:
if the integer in num_list is less than zero, the new list should not have
this value; if the integer in num_list is greater than or equal to zero and
odd, the new list should have this integer value with 10 added to it; if
the integer in num_list is greater than or equal to zero and even, the new
list should have this integer value unchanged;
>>> num_list = [-2, 33, 14, 6, -13, 9, 2]
>>> make_interesting_list(num_list)
[43, 14, 6, 19, 2]
"""
def make_interesting_list(num_list):
result = []
for value in num_list:
if value >= 0:
if (value % 2) == 1: # Check if value is odd
result.append(value * 10)
else:
result.append(value)
return result
Collapse List
Implement the following function:
def collapse(lst):
"""
Accepts a list of integers as a parameter and returns a new
list containing the result of replacing each pair of integers
with the sum of that pair.
If the list stores an odd number of elements, the final element
is not collapsed
>>> nums = [7, 2, 8, 9, 4, 13, 7, 1, 9, 10]
>>> collapse(nums)
[9, 17, 17, 8, 19]
>>> nums = [1, 2, 3, 4, 5]
>>> collapse(nums)
[3, 7, 5]
"""
def collapse(nums):
result = []
for i in range(len(nums)//2):
# since we're going pairs at a time, we only need
# to go as many as times as half the number of elements
result.append(nums[i * 2] + nums[i * 2 + 1])
if len(nums) % 2 == 1:
result.append(nums[-1])
return result
Rotate
Implement the following function:
def rotate_list_right(lst, n):
"""
returns a 'rotate' version of the list that rotates numbers
to the right n times. Each element in numbers is shifted
forward n places, and the last n elements are moved to
the start of the list.
Your function should not change the list that is passed as a
parameter.
>>> rotate_list_right([1, 2, 3, 4, 5], 2)
[4, 5, 1, 2, 3]
"""
def rotate_list_right(numbers, num):
"""
returns a 'rotate' version of the list that rotates numbers
to the right n times. Each element in numbers is shifted
forward n places, and the last n elements are moved to
the start of the list.
Your function should not change the list that is passed as a
parameter.
>>> rotate_list_right([1, 2, 3, 4, 5], 2)
[4, 5, 1, 2, 3]
"""
output_list = []
for i in range(len(numbers) - num, len(numbers)):
output_list.append(numbers[i])
for i in range(0, len(numbers) - num):
output_list.append(numbers[i])
return output_list
Images
Implement the following functions to familiarize yourself with the various ways you can work with images. Each function takes in a string filename, which can then be converted into a SimpleImage. Each function should end by displaying the image, which can be done by calling the .show() function on an image.
Double Left
Implement the following function:
def double_left(filename):
"""
Takes the left half of image, and copies it on top of the right half.
"""
Here is an example of the output of the function called on an image of Karel:
def double_left(filename):
"""
Takes the left half of image, and copies it on top of the right half.
"""
image = SimpleImage(filename)
mid_x = image.width // 2
for y in range(image.height):
for x in range(mid_x):
pixel = image.get_pixel(x, y)
pixel_right = image.get_pixel(mid_x + x, y)
pixel_right.red = pixel.red
pixel_right.green = pixel.green
pixel_right.blue = pixel.blue
image.show()
Squeeze Width
Implement the following function:
def squeeze_width(filename, n):
"""
A funhouse mirror effect governed by the int parameter n.
Create a new image with the same height as the original, but
with a width that is n times smaller than the original's.
Copy the original image such that it is squeezed horizontally.
For example, if n = 4, the pixels in the output image with an
x coordinate of 0 would be copies of the input pixels with
an x coordinate of 0 * 4 = 0, while pixels in the output
image with an x coordinate of 1 would be copies of the input
pixels with an x coordinate of 1 * 4 = 4, and so on.
"""
Here is an example run of what should happen:
def squeeze_width(filename, n):
image = SimpleImage(filename)
out = SimpleImage.blank(width=image.width // n, height=image.height)
for y in range(out.height):
for x in range(out.width):
pixel_out = out.get_pixel(x, y)
pixel = image.get_pixel(x * n, y)
pixel_out.red = pixel.red
pixel_out.green = pixel.green
pixel_out.blue = pixel.blue
out.show()
Crop Image
Implement the following function:
def crop_image(filename, n):
"""
crops the image to fit inside of a frame by shaving
n pixels from each side of the image.
You may assume that n is less than half the width of
the image.
"""
Here is an example of the output of the function called on an image of Karel:
def crop_image(filename, n):
"""
Takes the left half of image, and copies it on top of the right half.
"""
image = SimpleImage(filename)
out = SimpleImage.blank(image.width - 2 * n, image.height - 2 * n)
for y in range(out.height):
for x in range(out.width):
input_pixel = image.get_pixel(x + n, y + n)
out.set_pixel(x, y, input_pixel)
out.show()
Debugging with Doctests
In this problem, we'll work through using doctests to help us debug a program. Suppose we're trying to write a function which given two integers, calculates the Greatest Common Divisor of both numbers. The Greatest Common Divisor of two numbers is the largest number that is a factor in both of them. In greatest_common_divisor.py, we've provided the following buggy implementation of a function called greatest_common_divisor:
def greatest_common_divisor(a, b):
"""
Return the greatest common divisor of a and b.
>>> greatest_common_divisor(4, 16)
4
>>> greatest_common_divisor(16, 4)
4
>>> greatest_common_divisor(9, 24)
3
"""
lower = a
gcd = 1
for i in range(1, lower):
if a % i == 0 or b % i == 0:
gcd = i
At first glance, this looks like a reasonable way of solving the problem, but it turns out there are several issues. By running the doctests for the function, try identifying what the bugs are and correcting them. You might wish to add more doctests to more exhaustively test your function.
Here is a a fixed version of the program :)
def greatest_common_divisor(a, b):
"""
Return the greatest common divisor of a and b.
>>> greatest_common_divisor(4, 16)
4
>>> greatest_common_divisor(16, 4)
4
>>> greatest_common_divisor(9, 24)
3
"""
if a < b:
lower = a
else:
lower = b
gcd = 1
for i in range(1, lower + 1):
if a % i == 0 and b % i == 0:
gcd = i
return gcd