**Homework 3: Functions** **Reed CSCI 121 Spring 2026** *submit by 2/17 at 9am* This week we ask you, for each exercise, to write the code for a Python function rather than a Python script. You'll use the `def` statement to describe a function that computes and returns a value based on the values of the parameters that are passed to it. For example the third exercise has you write a function `duplicate`. You'll write code into a Python text file that includes a line like the following: ~~~ python def duplicate(text, width): ~~~ Here, `text` and `width` are the *formal parameters* that stand in for the *actual parameter* values sent to the function when it is used by other code. The lines after this `def` line describe the behavior of the function, *each line indented by 4 spaces*, ultimately *returning* a value that the function is asked to compute. (In the case of `duplicate`, the function will return a string of text characters.) Below, for example, is a function that performs temperature unit conversion, getting a temperature in degrees Fahrenheit, then reporting the equivalent in degrees Celsius: ~~~ python def convert(degrees_f): degrees_c = (degrees_f - 32.0) / 9.0 * 5.0 return degrees_c ~~~ Using this function's code is a bit different than running a script's code. We need to call the function in order to use its code. One way to test the code is to load the function definition into an *interactive* Python session, and then call the function with some parameters. If, for example, we imagine that the `convert` function is defined in a source file `convert.py`, we could do the following: ~~~ none jimfix@C02F48U1ML7H samples % python3 -i convert.py >>> convert(212.0) 100.0 >>> convert(32.0) 0.0 >>> convert(-40.0) -40.0 ~~~ Here, because we ran the Python command with `-i`, the code was loaded *interactively*, and we are able to test the function within the Python interpreter. When an exercise asks you to write a single function, it is more than okay to define *other* functions in the same `.py` file, ones that you use to help you compute the value expected for that one specified function. Indeed, writing these *helper functions* can sometimes be a great way to structure your code, especially since it allows you to test portions of your work---to get those subcomponents working---as you construct the function they serve. For many of these functions you'll want to use `if` statements and while statements to do their calculations. Also, you want to `return` the computed results rather than use `print` statements. Functions return values. --- **Lab Logistics** Today we will work on the first exercise as a group. And then I will pair you up for the second and third exercises. Switch roles so that each of you get to drive and to navigate. --- **[Hw3 Ex1] nth digit** `=>` *To be done as a group in lab. Hand it in individually.* Write a Python function `def nth_digit(number, position)`. It takes two integer parameters. The first parameter `number` is expected to be a positive integer. The second parameter `position` is also expected to be a positive integer. Counting from the right, this function should compute and return the digit of `number` at the position given by `position`. When `position` is 1, it should give the ones digit. When `position` is 2, it should give the tens digit. When `position` is 3, it should give the hundreds digit. And so on. Here below is an example of its use in the terminal: ~~~none terminal:homework reeduser$ python3 -i nth_digit.py >>> nth_digit(8734, 1) 4 >>> nth_digit(8734, 2) 3 >>> nth_digit(8734, 3) 7 >>> nth_digit(829463, 5) 2 >>> nth_digit(829463, 6) 8 >>> nth_digit(829463, 7) 0 ~~~ In the above, we define the function inside a file named `nth_digit.py`. (Any `.py` file name should work in Gradescope.) We load it into the Python interpreter so that we can test it by running `python3` with the `-i` flag. Note the last test of the function, where the 7th digit of a six digit number is reported as `0`. We think of numbers as having invisible zeros to the left of the digits we write. (i.e the number `82` has 0 hundreds, 0 thousands, etc.) **Note:** Do not convert the value of `number` to a string when writing this function. Also, do not use any floating point operations. --- **[Hw3 Ex2] duplicate** `=>` *To be done as a pair in lab. One person (the "driver") hands it in for your duo. If you miss lab hand it in on your own.* Write the function `duplicate`. It should take as its input parameters a string and an integer that represents an amount of available space. It should then return the longest string of repeats of the input string that fits in that space. For example: ~~~none >>> duplicate("x",6) 'xxxxxx' >>> duplicate("CSCI121", 20) 'CSCI121CSCI121' >>> duplicate("CSCI121", 22) 'CSCI121CSCI121CSCI121' >>> duplicate("computer", 5) '' >>> duplicate("XYZ", 16) 'XYZXYZXYZXYZXYZ' ~~~ **Hint hint:** remember your integer division operations! --- **[Hw3 Ex3] is multiple ** `=>` *To be done as a pair in lab. One person (the "driver") hands it in for your duo. If you miss lab hand it in on your own.* Write a Python function `def is_multiple(a,b)` that takes two integer values. It should return `True` whenever `a` is a multiple of `b`. It should return `False` otherwise. Here are sime sample uses of this function: ~~~ none >>> is_multiple(15, 3) True >>> is_multiple(20, 7) False >>> is_multiple(45, 1) True ~~~ In general, an integer is a multiple of some integer whenever it can be written as a product involving that integer and another. For example, since $$ 30 = 5 \times 6 $$ that means that 30 is a multiple of 5. Note that there will be some tricky cases here. What if `a` is negative? What if `b` is negative? What if one or both of the parameter are 0? You'll want to think carefully about the definition of "multiple" given above and make sure that what your code is doing is consistent with that definition. --- **[Hw3 Ex4] divisor report** `=>` *To be done on your own. Drive and navigate yourself. It's still okay to chat with others to discuss the work.* Write a function `divisor_report` that takes a positive integer. It should return a string that reports all the positive integer divisors of that number. That report should be in the form of a string that lists the divisors in order, starting with 1. If there are more than two divisors, it should use commas in the listing, including an Oxford comma before the mention of the last divisor. Here are sample uses of the function showing the kind of report we expect: ~~~none >>> divisor_report(35) 'The divisors of 35 are 1, 5, 7, and 35.' >>> divisor_report(1) 'The divisor of 1 is 1.' >>> x = divisor_report(7) >>> x 'The divisors of 7 are 1 and 7.' >>> divisor_report(9) 'The divisors of 9 are 1, 3, and 9.' ~~~ **Hint:** Recall that you can initialize a string variable to hold some text and then concatenate more text onto its end, like so: ~~~none >>> s = "hello" >>> s = s + " w" >>> s = s + "orld" >>> s = s + "!!!" >>> s 'hello world!!!' >>> ~~~ Your code for `divisor_report` can do something similar, say, as part of a loop, with code before the loop, and with code after a loop. --- **[Hw3 Ex5] mult table** `=>` *To be done on your own. Drive and navigate yourself. It's still okay to chat with others to discuss the work.* In the last homework you wrote a script that output a multiplication table using `print`. For this exercise, we instead want you to write a function that builds and returns a text string containing the characters of the table. Write a function `def mult_table(rows, columns)` that returns a string that represents a multiplication table of the given dimensions. For example below are two applications of this function: ~~~none >>> mult_table(3,2) '1|2|\n-+-+\n2|4|\n-+-+\n3|6|\n-+-+\n' >>> mult_table(2,5) ' 1| 2| 3| 4| 5|\n--+--+--+--+--+\n 2| 4| 6| 8|10|\n--+--+--+--+--+\n' >>> ~~~ Just like the script, the text string contains lines like ~~~none 2| 4| 6| 8|10| ~~~ but the string of text for this line should end with an newline character `'\n'`. The numbers reported should have leading spaces to make the width of each column the same. These table entry strings should alternate with line separator strings like ~~~none --+--+--+--+--+ ~~~ These should also end with a `'\n'` character. The function does not *print* these strings. Rather it just *returns* the string back. It is possible to then `print` them, like so: ~~~none >>> table3x4 = mult_table(3,4) >>> print(table3x4) 1| 2| 3| 4| --+--+--+--+ 2| 4| 6| 8| --+--+--+--+ 3| 6| 9|12| --+--+--+--+ >>> ~~~ Notice that there will be an extra blank line if you print a multiplication table's string. Each of the rows ends with a newline character. And also the `print` issues a newline character after it prints each of the table's lines. --- **[Hw3 Ex6] poly2max** `=>` *To be done on your own. Drive and navigate yourself. It's still okay to chat with others to discuss the work.* Consider the expression $$ z = -x^4 + 3x^2 - y^4 + 5y^2. $$ We are interested in the values it takes when $x$ and $y$ are integers. Write a function `poly2max` that takes as input minimum and maximum values for $x$ and $y$. It should return the maximum value $z$ can be when $x$ and $y$ are in those specified ranges. For example, the result of `poly2max(0, 5, 3, 10)` should be the maximum value that $z$ can take when $x$ is at least 0 and at most 5, and $y$ is at least 3 and at most 10. (This maximum value happens to be -34.) ~~~none >>> poly2max(0, 5, 3, 10) -34 >>> poly2max(0, 5, 0, 10) 6 >>> poly2max(-6, -3, -4, -2) -50 ~~~ --- **[Hw3 Ex7] collatz ** `=>` *To be done on your own. Drive and navigate yourself. It's still okay to chat with others to discuss the work.* Consider the following process. Start with some positive integer. If the number is odd, multiply it by 3 and add 1. If instead it is even, divide it by 2. For every number that has ever been tried, this process eventually leads to the number 1. (It is an open problem, the Collatz Conjecture, whether this is true for all integers.) For example, starting with 6 yields the sequence 6, 3, 10, 5, 16, 8, 4, 2, 1. That is a sequence of length 9. Your goal for this exercise is to write code that will search for very long Collatz sequences. Write a function `collatz` that takes an integer `n`. It should return the smallest starting integer whose generated sequence is at least `n` in length. Here are some examples: ~~~none >>> collatz(11) 7 >>> collatz(8) 3 >>> collatz(100) 27 ~~~ **Note:** This function *is not returning the length of the sequence* that starts at `n`. For example, `collatz(11)` returns 7, because the Collatz sequence starting at 7 is the first one that is at least 11 numbers long. **Hint:** As you develop *this* function, you might find it *really* useful to write a "helper function" that computes the length of the Collatz sequence starting with some number. And, as you develop *that* function, you might practice by having *it* print out the Collatz sequence, just to make sure you are getting things right. If that function is called `helper` you can test it with `helper(6)` and other values to see if everything is working correctly.