# Homework 5: Containers¶

## Objectives¶

• Gain experience working with the membership operator (in) and identity operator (is).
• Solve problems using the following container types: list, tuple, set, dictionary.

Submissions Limited

From now on, the number of submissions on Gradescope will be limited. For HW5, you may submit each exercise up to 20 times. Before you submit to Gradescope, make sure that you:

1. Test your code, either in the shell or in the __main__ block.
2. Run !flake8, and resolve all PEP 8 and docstring issues.

## Exercise 5.1 Object Comparisons¶

For this exercise you will write a function that will evaluate any two Python objects in order to provide a numeric score indicating how closely related they are. Create a file named compare.py with a single function named compare. This function must take two arguments, and must return an integer satisfying the following requirements:

• If the arguments contain references to the same object, return 0.
• If the arguments refer to different objects with same type and same value, return 1.
• If the arguments are the same type, but have different values, return 2.
• If the arguments are of different types, return 3.

The following interaction provides some examples of the expected behavior.

>>> a = "#1"
>>> b = a
>>> c = "#"
>>> c += "1"
>>> d = "#2"
>>> e = 7
>>> compare(a, b)  # Same object.
0
>>> compare(a, c)  # Different objects the same value.
1
>>> compare(c, d)  # Same type, different values.
2
>>> compare(d, e)  # Different types.
3

## Exercise 5.2 Sort Short¶

Create a file named sorting.py that provides two functions: swap and sort_short.

### swap¶

The swap function must take three arguments: a list, and two integers representing indices into the list. It must then swap the the list elements stored at the provided indices. It should not have any return value.

Here is an example of the expected behavior:

>>> items = ["a", "b", "c"] >>> swap(items, 0, 2) >>> print(items) ['c', 'b', 'a']

### sort_short¶

The sort_short function must take a single list argument containing numeric values. The list will have no more than three items. This function must rearrange the items in the list so that they appear in increasing order. Your implementation should call the swap method as needed to move items within the list. For example, if we knew that the list would have exactly two items, the following implementation would be correct:

def sort_short(items):
if items[0] > items[1]:
swap(items, 0, 1)

There is no return value. You may assume that the list will not contain duplicates.

Here is an example illustrating how we can use the assert statement to check for the correct behavior:

if __name__ == "__main__":

items = [1]
sort_short(items)
assert items == [1]

items = [2, 1]
sort_short(items)
assert items == [1, 2]

items = [3, 2, 1]
sort_short(items)
assert items == [1, 2, 3]

The assert statement provides a built-in mechanism for confirming that our code is working as expected. If the expression after assert evaluates to True, then nothing happens, if it evaluates to False, then there will be an AssertionError indicating that there is something wrong with our implementation (or with our test).

Your solution must not use any loops, and it must not use any built-in sorting functions.

## Exercise 5.3 Image Manipulations¶

Computers typically represent images as a grid of pixels where each pixel is a tiny rectangular region of a particular color. The color of a pixel may be represented as a three-entry tuple where the first entry represents the amount of red, the second represents the amount of green and the third represents the amount of blue. These three color values (often referred to as channels) are restricted to be in the range 0–255. For example:

# Create some variables to store tuples representing common colors...
red = (255, 0, 0)
green = (0, 255, 0)
blue = (0, 0, 255)
black = (0, 0, 0)
white = (255, 255, 255)

Complete the following two functions for working with color values. These should both be in a file named color_utils.py.

This function will take two parameters: the first is a length-three tuple representing a color. The second is an integer representing a value (positive or negative) that will be added to each color channel. The return value must be a tuple where each color channel has been adjusted by the requested quantity, with values clipped to stay in the range 0-255. For example:

>>> %Run color_utils.py >>> adjust_brightness((0, 0, 250), 10) (10, 10, 255) >>> adjust_brightness((100, 5, 255), -10) (90, 0, 245)

Advice: As you program this function, you will probably find yourself repeating much of the code three times: once for each color channel. This is a good opportunity to use a helper function. Helper functions are functions that simplify the implementation, but aren't explicitly required by the specification. In this case, it would be useful to have a helper function named adjust_channel that would handle the logic for increasing or decreasing the value of a single color. You can then avoid code duplication by calling this function three times in adjust_brightness.

### same_color¶

This function returns a Boolean value indicating whether two provided color tuples are similar enough to be considered the same color. It must take three parameters: the first two are color tuples, and the third is an integer value representing the maximum allowable difference between corresponding channels in the two color tuples. For example:

>>> same_color((128, 25, 80), (128, 25, 80), 0) True >>>> same_color((129, 25, 80), (128, 25, 80), 0) # Red differs by 1! False >>>> same_color((129, 25, 80), (128, 25, 80), 1) # Allow a difference of 1! True

Just for fun: We've provided some simple demo programs that you can use to try out these functions once you have them working. In order to execute these programs, store then in the same directory as your color_utils.py file and run them using Thonny. You will need to install the Pillow (Python Imaging Library) package through the Thonny package manager.

• adjust_brightness.py: This application allows the user to select an image (which must be in JPG format), and increase or decrease the brightness by a user-specified amount. This is accomplished by applying your adjust_brightness function to each pixel.

• green_screen.py: This application allows the user to select an image with a green-screen background and a second image that will be used as a background replacement. It then applies a green-screen effect by checking each pixel in the original image against the green-screen color using your same_color function. Pixels that are determined to be the same color as the green screen are replaced with the corresponding pixel from the background image. You can test the application using the following two images. Right click them and download them into the same folder as your Python code.

If you want to create your own green-screen images, this web-based image viewer will allow you to view the colors of individual pixels so you can determine the appropriate color values for the "green" in your background: https://imagecolorpicker.com.

## Exercise 5.4 Basketball Scores¶

The JMU basketball program needs help calculating player statistics. They would like you to write them a program, named scores.py, containing two functions:

• player_stats This function must take a list argument containing a sequence of integers, where each integer represents the total points scored by a particular player in a single game. It must return a dictionary with the following key/value pairs:
• The "total" key must be mapped to the player's total points for the season.
• The "average" key must be mapped to the player's average points per game.
• The "zeros" key must be mapped to the number of times the player scored 0 points in a game.
• The "final" key must be mapped to the number of points the player scored in the final game of the season.
• print_stats This function must take two arguments: a string representing a player name and a list argument containing a sequence of integers representing the points scored by a particular player. It should then print out a nicely formatted report with each of the statistics provided by the player_stats method. This function must not include the logic for calculating the various statistics. Instead, it must call player_stats and make use of the returned dictionary.

Here is an example illustrating the expected behavior of player_stats:

>>> stats = player_stats([19, 6, 12, 22, 22, 17]) >>> print(stats) {'total': 98, 'average': 16.333333333333332, 'zeros': 0, 'final': 17}

Here is an example illustrating the expected behavior of print_stats:

>>> print_stats("Kiki Jefferson", [19, 6, 12, 22, 22, 17]) --Kiki Jefferson-- Total points: 98 Average points: 16.3 Number of 0 point games: 0 Final game score: 17

Notice that each statistic is indented two spaces and the average points value is rounded to one decimal place.

Both functions should work correctly for any list of integers of length at least 1. You should test your functions with several different integer lists to make sure that they behave correctly in each case.

Note: You must not use loops in your program. Programs that use loops will receive no credit.

## Exercise 5.5 Assigning Tasks¶

For this exercise you will write Python code to assist in fairly dividing up tasks between two roommates. You must create a file named tasks.py assign.py (updated 9/24) that contains a single function named assign_task.

The assign_task function must take three arguments: a string representing the new task, and two sets containing the tasks that have already been assigned. The function must return a Boolean indicating whether the resulting assignment is fair. A fair assignment is any assignment such that number of elements in each set differs by at most one. Task assignments must satisfy the following requirements:

• There can be no duplicate tasks. If a task already exists in either set, it must be ignored. You may assume that there will not be duplicate tasks in the provided sets.
• New tasks must be added to the set that currently has fewest items. In the case of a tie, the task must be added to the set associated with the first set argument.

The following interaction provides some examples of the expected behavior:

>>> my_work = set(["sweep", "windows", "laundry"])
>>> your_work = set()
>>> assign_task("tidy", my_work, your_work)
False
>>> print(my_work, your_work)
{'windows', 'sweep', 'laundry'} {'tidy'}
>>> assign_task("sweep", my_work, your_work)
False
>>> print(my_work, your_work)
{'windows', 'sweep', 'laundry'} {'tidy'}
>>> assign_task("garbage", my_work, your_work)
True