You know the drill. Write the functions. Paste in the unit test underneath. (You can also go to this CodeHS project and fork it.) Run and then take a look at the report.txt file.
As always, the unit test will crash if you haven't defined all the functions. So I suggest that to begin you create them. Like this:
def pw_def():
pass
def abs_val():
pass
def implies():
pass
Etc.
Wikipedia, in its article on piecewise-defined functions, gives the example to the side.
Write me a function named pw_def that returns the same values as this function.
Test Cases:
>>> pw_def(-10)
7
>>> pw_def(-3)
0
>>> pw_def(-1)
2
>>> pw_def(0)
3
>>> pw_def(2)
-1
>>> pw_def(3)
-3
>>> pw_def(10)
0.5
In Chapter 2, you learned how to compute absolute value by means of the square and square root functions. Now do it with if-elif-else. The idea of course is that the absolute value of x is just x when x is 0 or greater and that the absolute value of x is -x when x is less then 0. Call your function abs_val.
Test Cases:
>>> abs_val(-2)
2
>>> abs_val(0)
0
>>> abs_val(3)
3
Python has three built-in Boolean functions. (Recall that a Boolean function takes as input one or more Booleans and produces as output a Boolean.) These are and, or and not. Let's build some more! Each of the functions below can have a body that's one line long. I challenge you to do that! Your will of course make use of one or more of the built-in Boolean functions in your work. As always, please feel free to have later functions call earlier ones. (If you're curious about what all these are and what significance they might have, start here.)
Note: as of 4/14/2025, the Unit Test does not properly test these functions.
Write a function named NOR that takes two Booleans and returns True when and only when both are False. ("NOR" is short for "neither nor".)
Write a function named NAND that takes two Booleans and returns True when and only when at least one input is False. ("NAND is short for "not and".)
Write a function named XOR that takes two Booleans and returns True when and only when one is True and the other False. ("XOR" is short for "exclusive or".)
Write a function named XNOR that takes two Booleans and returns True when and only when the inputs are the same. ("XNOR" is short for "exclusive nor".)
Write a function named entails that takes two Booleans and returns False when and only when the first is True and the second False.
Write a function named sign that takes a number and returns -1 if that number is less than zero, 0 if it's equal to zero and 1 if it's greater than 0.
Test Cases:
>>> sign(-12)
-1
>>> sign(0)
0
>>> sign(3)
1
Write a function that counts and returns the number of negatives, the number of zeros and and the number of positives in a list of numbers. Call it sign_count. The return statement at its end will be (something like) return num_neg, num_zero, num_pos. As we saw in the last chapter, we can return multiple values if we wish; simply separate them by a comma.
Test Cases:
>>> sign_count([])
(0, 0, 0)
>>> sign_count([1, -1, 0])
(1, 1, 1)
>>> sign_count([1, -1, 1, 0])
(1, 1, 2)
>>> sign_count([0, 0, 1, -1, 1, 0, -1])
(2, 3, 2)
In the first test case, we sent sign_count the empty list.
You will of course have to iterate through the input list with for. If the list is L, you'll have a line like for n in L:.
Please do call the function sign in the body of sign_count. If you already have a function to do a thing, don't rewrite it elsewhere; just call it!
Write a function named letter_grade which takes a numerical grade and returns the associated letter grade. Use the usual ranges - an 'A' for any grade from 90 to 100 inclusive, a 'B' for any grade greater than or equal to 80 and less than 90, etc. Return 'NaG' for any value outside the range 0 - 100.
Don't just stack if's. Use the full if-elif-else structure.
Test Cases:
>>> letter_grade(-1)
NaG
>>> letter_grade(100)
A
>>> letter_grade(89.9)
B
Write a function named letter_grade_plus that takes a numerical grade and returns the associated letter grade with a "+" or a "-". Let's say that a grade in the range [99, 100] is an A+, a grade in the range [90, 91] is an A-, a grade in the range [89, 90) is a B+, etc. (Remember that we use a square brackets when we wish to include a value and a parentheses when we don't. So [99, 100] includes 99 and 100, and [89, 90) includes 89 but not 90.)
Here's a challenge for you: write your code so that only one line adds the "+" or "-".
Test Cases:
>>> letter_grade_plus(99.2)
'A+'
>>> letter_grade_plus(90)
'A-'
>>> letter_grade_plus(89.99)
'B+'
>>> letter_grade_plus(60)
'D-'
>>> letter_grade_plus(59.99)
'F'
Let's say we're given three numbers a, b and c and wish to decide whether a triangle can exist whose side lengths are a, b and c. A lovely little theorem in geometry says this:
If all are positive and the sum of any two is greater than the third, then they can be the side lengths of a triangle; otherwise they cannot.
Write Boolean-valued function named is_tri that takes three numbers and returns True if they can be the side lengths of a triangle and False if they cannot.
Test Cases:
>>> is_tri(-1, 4, 2)
False
>>> is_tri(1, 1, 1)
True
>>> is_tri(1, 2, 1)
False
>>> is_tri(5, 3, 4)
True
>>> is_tri(9, 3, 4)
False
Write a function named order_three that takes three numbers and returns the three in order from greatest to least. (The final line of the function might look like return largest, middle, smallest.) Good for you if you know that Python has a built-in sort function. Don't use it!
Test Cases:
>>> order_three(1, 2, 3)
(3, 2, 1)
>>> order_three(13, 5, 12)
(13, 12, 5)
Write a function names extrema that takes a list of integers that returns the greatest and the least of those integers. Return greatest first, least second.
Don't use a built-in function. Don't import a module. You can do this with what you've learned in chapters 1 - 4.
If you know about indices and how they're used to pull particular items out of a list, you might think you need them here. You do not.
You likely have a variable with a name like cur_max that holds the greatest value found so far. (You'll have a cur_min or something like it too of course.) The challenge here is to figure out how to assign an initial value to cur_max. Let me give you a hint. One use for variables is as flags. Their value can answer the question, Has a certain even happened? A Boolean is often a good choice here. Maybe set the flag variable to False initially and then, we the event happens, flip it to True.
>>> extrema([2, -3, -4, 7, 12, 0])
(12, -4)
>>> extrema([2])
(2, 2)
Assume that the three sides of a triangle are a, b and c and that c is at least as great as a or b. If a2 + b2 = c2, then the triangle is right. If a2 + b2 > c2, then the triangle is acute. If a2 + b2 < c2, then the triangle is obtuse.
Write a function named tri_test that takes the three side lengths of a triangle and returns one of four possible values: 'right', 'acute', 'obtuse' or 'NaT'. (Those are strings of course. The last of the four stands for 'Not a Triangle'.)
You cannot assume that the inputs will come in order from least to greatest. You'll probably want to order the inputs first. Hmm ... maybe a bit of code written above will be of help. You do remember, don't you, that a function can call another function?
Arithmetic with floats is not always exactly accurate, so it's not safe to test floats for equality. Instead of a test for equality, test whether the floats are close enough. How? Perhaps test whether the absolute value of their difference is less than, say, 0.0000001. So here the test should be abs(a**2 + b**2 - c**2) < 0.0000001, not a**2 + b**2 == c**2.
Test Cases:
>>> tri_test(3, 4, 5)
right
>>> tri_test(3, 4, 6)
obtuse
>>> tri_test(3, 4, 4)
acute
>>> tri_test(3, 4, 500)
NaT
>>> tri_test(-3, 4, 5)
NaT
Write a function named digit_count that takes a positive integer and returns its number of digits. Use iteration. Do not convert to a string. Do not use the log function. (Don't worry if you don't know what that is.) Hint: // 10.
Test Cases:
>>> digit_count(7)
1
>>> digit_count(1273)
4
A year is a leap year if it is divisible by 4 unless it is a century that is not divisible by 400. Examples: 2012 is a leap year (divisible by 4 and not a century), 2100 is not a leap year (divisible by 4 but is a century divisible not divisible by 400), 2000 is a leap year (divisible by 4 and is a century divisible by 400). Write a function named leap_year that takes a year as its input and returns True if the year is a leap year and False otherwise.
How do you test for divisibility? m is divisible by n if the remainder when m is divided by n is 0. In Python, that's m % n == 0.
Test Cases:
>>> leap_year(1969)
False
>>> leap_year(1968)
True
>>> leap_year(1900)
False
>>> leap_year(1600)
True
Let's say we wish to approximate the square root of a number n. Here's a method:
Make a guess. (How you generate that initial guess is irrelevant. Simply divide n in half if you like.) Call it cur_guess perhaps.
Now refine that guess. How? If cur_guess is the name of the current guess, the new guess will be (1/2) * (cur_guess + n/cur_guess). I assume you'll call the new guess new_guess.
Now, if cur_guess and new_guess are sufficiently close in value, stop and return the value of new_guess. Otherwise return to Step 2 (after of course you make new guess the value of current guess).
Write a function named sq_rt that implements this algorithm. It should take a positive integer and return an approximation of its square root.
So, what's the "close enough" referred to in Step 3 above? My suggestion is that you don't stop until the absolute value of the difference of the new guess and the current guess is less than some small value, say 0.0000001.
Test Cases:
>>> sq_rt(4)
2.0
>>> sq_rt(12)
3.4641016151377544
An integer is prime if it's positive and has precisely two divisors. The sequence of primes begins 2, 3, 5, 7, 11, ... . Write a function named is_prime that takes an integer n and returns True if it's prime and False otherwise.
You could of course check for divisibility by all integers from 2 to n - 1. But that's massively inefficient. I'm sure you can do better.
Test Cases:
>>> is_prime(2)
True
>>> is_prime(3)
True
>>> is_prime(4)
False
>>> is_prime(89)
True
>>> is_prime(99)
False
Below is the so-called "Hailstone Algorithm".
Choose a positive integer n (which we'll call the "seed" of the sequence we'll build):
If n equals 1, terminate the algorithm.
If n is even, cut it in half.
If n is odd, triple it and add 1.
Return to step 2.
Here's an example of a sequence generated by seed 12:
12 → 6 → 3 → 10 → 5 → 16 → 8 → 4 → 2 → 1.
Write a function named hailstone that takes a positive integer n and returns the associated hailstone sequence in list form.
You'll use integer division (//), not float division (/). Why? Whenever we divide in the algorithm above, we divide an even integer by 2, and the result of that must be an integer. If you use float division, you'll introduce slight errors into your results; and if the loop runs long enough, those errors will accumulate and you'll start to get wrong results.
How can you build up a list? One way is the list concatenation operator +, which takes a pair of lists and returns and list. Examples:
>>> L = []
>>> L = L + [1]
>>> L
[1]
>>> L = L + [2]
>>> L
[1, 2]
We initialized L on the first line; it's initial value was the empty list. We then concatenated L with the list [1], and the result was [1]. Last we concatenated L with [2], and the result was [1, 2].
This is precisely what you'll do in your fuction. Initialize a varible to the empty list before you enter the while loop; and then when you find the next member of the sequence, concatenate it to your list. So if your list is L and the new member of the sequence is n, you'll have the line L = L + [n].
Do be careful. Only lists can be concatenated to lists. So L + n will return an eror if n is some number.
Test Case:
>>> hailstone(12)
[12, 6, 3, 10, 5, 16, 8, 4, 2, 1]
Here's a pretty little algorithm that computes the least common multiple (LCM) of a pair of positive integers p and q.
Set m equal to p and n equal to q.
If m and n are equal, stop and return m. Otherwise continue.
If m is less than n, increase m by p. If n is less than m, increase n by q.
Return to 2.
Write a function named LCM that implements this algorithm. Its inputs are a pair of positive integers. Its output is their LCM.
Test Cases:
>>> LCM(2, 3)
6
>>> LCM(3, 3)
3
>>> LCM(3, 15)
15
>>> LCM(9, 12)
36
We did LCM. We should also do GCF (Greatest Common Factor). The algorithm is below. The two positive integers whose GCF we wish to find are p and q.
Set m equal to the smaller of p and q; set n equal to the larger of p and q.
If n is divisible by m, stop and return m.
Otherwise set n equal to m and m equal to n modulus m.
Return to 2.
Write a function that implements this algorithm. Name it GCF. Its inputs are two positive integers. Its output is their GCF.
A few tips:
m modulus n is the remainder when m is divided by n. Examples: 12 modulus 4 is 0, and 12 modulus 5 is 2.
The percent symbol is Python's modulus operator.
You can't code step 4 of the algorithm like this: n = m and then m = n % m. Why not? We need to set m to n % m with the old value of n, the value of n before these two lines. But the first assignment changes n to a new value. I'll let you figure out how to get around this. (You might try the Google search "python assign two variables at once".)
Test Cases:
>>> GCF(2, 3)
1
>>> GCF(3, 3)
3
>>> GCF(30, 42)
6
Write a function named reduce_frac that takes the numerator and denominator of a fraction and returns the numerator and denominator of that fraction in reduced form. You should find GCF useful in this.
Test Cases:
>>> reduce_frac(12, 15)
(4, 5)
>>> reduce_frac(12, 17)
(12, 17)
>>> reduce_frac(12, 12)
(1, 1)
Write a function named add_fracs that takes two fractions and returns their sum in reduced form.
I'll help a bit. Begin your function like this:
def add_fracs(numer1, denom1, numer2, denom2):
Your function should end with the line:
return sum_numer, sum_denom
How will you do this? Why, just as you did when you learned to add fractions. Find a common denominator, write the two fractions with that common denominator, add and then reduce. You'll use both your LCM and your GCF function.
Test Cases:
>>> add_fracs(1, 2, 1, 3)
(5, 6)
>>> add_fracs(5, 6, 12, 12)
(11, 6)
Assume that (a, b, c) is a triple of positive integers arranged from least to greatest. That triple is a Pythagorean Triple if a**2 + b**2 == c**2. For instance, (3, 4, 5) is a Pythagorean Triple, since they are given in order of size and 3**2 + 4**2 == 5**2.
Write a function named pyth_trip with parameter n that returns the number of Pythagorean triples in which all of a, b and c are less then or equal to n. Don't overcount! If for instance you count (3, 4, 5), don't also count (4, 3, 5) or any other arrangement of the 3, 4 and 5.
If you send the function the integer 10, it should find the Pythagorean Triples (3, 4, 5) and (6, 8 , 10) and thus should return 2.
The real challenge here is to write a function that will execute in a reasonable time for a reasonable input. What's a reasonable time? A few seconds. What's a reasonable input? 1000 is certainly reasonable. For an input of 1000, my function returned the answer in about 2 seconds.
Test Cases:
>>> pyth_trip(10)
2
>>> pyth_trip(50)
20
>>> pyth_trip(100)
52