**Homework 3: Functions** **Reed CSCI 121 Spring 2025** *submit by 2/18 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 fifth exercise has you write a function `mult_table`. You'll write code into a Python text file that includes a line ~~~ python def mult_table(rows, columns): ~~~ Here, `rows` and `columns` 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 `mult_table`, the function will return a string of 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. --- **[Hw3 Ex1] nth digit** 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] is multiple ** 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 Ex3] duplicate** Write the function `duplicate`. It should take as its inputs 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' ~~~ --- **[Hw3 Ex4] divisor report** 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.' ~~~ --- **[Hw3 Ex5] poly2max** 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 Ex6] mult table** Write a function `def mult_table(rows, columns)` that returns a string that represents a multiplication table of the given dimensions. For example: ~~~ >>> print(mult_table(8,14)) 1| 2| 3| 4| 5| 6| 7| 8| 9| 10| 11| 12| 13| 14| ---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2| 4| 6| 8| 10| 12| 14| 16| 18| 20| 22| 24| 26| 28| ---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 3| 6| 9| 12| 15| 18| 21| 24| 27| 30| 33| 36| 39| 42| ---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 4| 8| 12| 16| 20| 24| 28| 32| 36| 40| 44| 48| 52| 56| ---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 5| 10| 15| 20| 25| 30| 35| 40| 45| 50| 55| 60| 65| 70| ---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 6| 12| 18| 24| 30| 36| 42| 48| 54| 60| 66| 72| 78| 84| ---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 7| 14| 21| 28| 35| 42| 49| 56| 63| 70| 77| 84| 91| 98| ---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 8| 16| 24| 32| 40| 48| 56| 64| 72| 80| 88| 96|104|112| ---+---+---+---+---+---+---+---+---+---+---+---+---+---+ >>> ~~~ The rows and columns should be divided up using the minus, plus, and bar characters `'-'`, `'+'`, and `'|'`. It should also include the end-of-line character `'\n'` at each row's end. The numbers reported should have leading spaces to make the width of each column the same. Here are two more sample runs just showing the strings returned. ~~~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' >>> ~~~ --- **[Hw3 Ex7] collatz ** 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: ~~~ >>> 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.