**Homework 4: Lists and Function Parameters** **Reed CSCI 121 Spring 2025**   *submit by 2/25 at 9am* In all of the exercises below, each function you are asked to write either processes a Python list (or lists), or returns a Python list, or both. To solve them, I would like you to restrict your use of the list constructs we covered in lecture. This will ensure that you get practice with these particularly, and it will set you up to solve a particular puzzle that will help your programming. As the weeks progress, you can explore the wide variety of other built-in libraries and constructs for working with lists. For now, though, I'd like you to work with what we've covered by Monday's lecture. Here is a summary of **things you can use**: * using an index to access the value in a list * computing the length of a list * modifying a list item at an index by indexed assignment * creating a list by listing its values in brackets * appending to, extending, and inserting into a list * popping a value from the end of a list, or deleting within it by index * checking list contents with `==` and `in` These are summarized on [a lecture slide from Monday](hw4-list-constructs.pdf). In particular, for this homework I'd like you to avoid using `for` loops (use only `while`) and also *list comprehensions*, among other things. These are powerful constructs, or at least seemingly convenient, and they can be used well. But I've also found people adopt the use of them too early, and never get used to (or get practice with) the more bread-and-butter operations for scanning or constructing a list. Notably missing are these **things you cannot not use** yet: * the `for` loop * list comprehensions Also, I'd like you to be wary of whether the work you do with a list parameter has the side-effect of changing (i.e. "mutating") the list. Unless the problem explicitly asks you to write a function that changes a list, your code should not change a list it is given as a parameter. (###) Function parameters Exercises 4 and 5 are our first practice using function variables. Each of them takes a function as one of its parameters. To test whether they work, you'll need to write other functions and pass them as a parameter-- various two-value operators for exercise 4, functions that check a value and return a boolean for exercise 5. Hopefully the examples in their descriptions and the examples from lecture will help you play with this Python feature. --- **[Hw4 Ex1] squares** *(To be done as a group in lab.)* Write a function `squares` that takes a list of integers and returns a new list of their squares. ~~~none >>> squares([1, 12, 3, 4, 12]) [1, 144, 9, 16, 144] >>> squares([]) [] >>> squares(7) [49] ~~~ --- **[Hw4 Ex2] down up** Write a function `down_up` that takes a positive integer parameter. It should return a list that counts from that number down to 1 and then back up again. ~~~none >>> down_up(3) [3, 2, 1, 2, 3] >>> down_up(5) [5, 4, 3, 2, 1, 2, 3, 4, 5] >>> down_up(1) [1] ~~~ --- **[Hw4 Ex3] make even** Write a procedure `make_even` that, when given a list of integers, modifies the elements of the list to make them all divisible by 2. If a list element is even, it should not be changed. If a list element is odd, it should be decreased by one. ~~~none >>> ys = [1, 3, 2, -7, 0, -8, 11] >>> zs = [10, 0, 1, 1, -1, 100] >>> make_even(ys) >>> make_even(zs) >>> ys [0, 2, 2, -8, 0, -8, 10] >>> zs [10, 0, 0, 0, -2, 100] >>> ~~~ Note that the `make_even` changes the list it is given. It returns the value `None`. The list modification is a *side effect* of it being executed. --- **[Hw4 Ex4] combine using** Write a function `combine_using(list1, list2, op)` that takes two lists as input. The lists are expected to be of the same length. Its third parameter `op` is expected to be a two-parameter function. The first parameter of `op` is assumed to handle values that are in `list1`. The second parameter of `op` is assumed to handle values that are in `list2`. The function `combine_using` will use `op` to combine pairs of items in the list, and build a third list of those results. It might be best to show some examples of its use: ~~~none >>> from operator import mul >>> mul(10,7) 70 >>> mul("x",6) 'xxxxxx' >>> combine_using([10, 3, 1, -1], [7, 4, 19, 19], mul) [70, 12, 19, -19] >>> combine_using(["x", "yo", "!", "nope"], [6, 2, 10, 0], mul) ['xxxxxx', 'yoyo', '!!!!!!!!!!', ''] ~~~ Here we've imported the two parameter function `mul` that applies the `*` operator to its two parameters. The first example has the results of the four products `10 * 7`, `3 * 4`, `1 * 19`, and `-1 * 19`. The second example has string repetitions-- six repeats of `x`, two repeats of `yo`, ten repeats of `!`, and no repeats of `nope`. --- **[Hw4 Ex5] first with** Write a function `first_with(property_test, values, how_many)` that takes three parameters. It processes a list in search of values that have a certain property. The first paramater `property_test` is a function that it uses to check each value. It takes a value as input and returns a boolean of `True` or `False` for any value it is given. The second is a list of values that it will test, starting from the front of the list, using the function `property_test`. The function should scan through `values` and build a new list containing only those values that make `property_test` return `True`. Furthermore, it should include no more than `how_many` of those values, the first ones that make the test `True`. That `how_many` will be a non-negative integer, possibly 0. Here are some example uses of the function just below: ~~~none >>> first_with(is_even, [1, 12, 3, 4, 12, -6, 1, 8], 4) [12, 4, 12, -6] >>> first_with(is_even, [1, 12, 3, 4, 12, -6, 1, 8], 3) [12, 4, 12] >>> first_with(is_even, [1, 12, 3, 4, 12, -6, 1, 8], 10) [12, 4, 12, -6, 8] >>> first_with(is_lowercase, ['a', 'B', 'c', 'd', 'E', 'z', 'M'], 2) ['a', 'c'] >>> first_with(is_lowercase, ['a', 'B', 'c', 'd', 'E', 'z', 'M'], 0) [] ~~~ In these examples above, `is_even` is a function that returns `True` when given an even integer and returns `False` when given an odd integer. The function `is_lowercase` is one that returns `True` when given a letter in a string that is lowercase. It returns `False` when given a letter in a string that is uppercase. Here, for you reference, are how those two property testing functions might be written: ~~~python def is_even(x): return (x % 2 == 0) def is_lowercase(s): return (s == s.lower()) ~~~ --- **[Hw4 Ex6] round robin list** You are organizing a tournament for a dice strategy game. In the round-robin stage of the tournament, each player plays each other player exactly once. Write a function `round_robin` that takes as input a list of players. It should return a list of match-ups for all the games to be played. Each pairing of players in a match should be included as a list of length two. ~~~none >>> players = ["Jim", "Bob", "Sisy"] >>> round_robin(players) [["Jim", "Bob"], ["Jim", "Sisy"], ["Bob", "Sisy"]] ~~~ The ordering within each pair doesn't have to match ours above, nor does the ordering amongst the pairs. This means that the following would also be fine (though I can't quite imagine the code that would produce it): ~~~none >>> players = ["Jim", "Bob", "Sisy"] >>> round_robin(players) [["Bob", "Sisy"], ["Bob", "Jim"], ["Jim", "Sisy"]] ~~~ What's important is that every possible match-up occurs exactly once. In producing this list, your function can assume there are no duplicate names in the list of players. --- **[Hw4 Ex7] digit buckets** Write a function `digit_buckets` that takes a list of integers. It should return a list of lists, one list for each digit from 0 to 9. The 0-th list should contain all the integers that end with 0. The 1st list should contain all the integers that end with 1. The 2nd list should contain all the integers that end with 2. Etc. ~~~none >>> digit_buckets([123, 87, 45, 55, 7, 36, 317, 90, 909, 201, 7]) [[90], [201], [], [123], [], [45, 55], [36], [87, 7, 317, 7], [], [909]] ~~~ The order that the numbers appear in each list should be the same as their relative order in the original list.