Chp 4 Functions

Instructions

You know the drill. Write the functions. Paste in the unit test underneath. 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

def neither_nor():

    pass

def sign():

    pass

Etc.

A Piecewise-Defined Function

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

Absolute Value Again

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

Implies

Write a function named implies that takes two Booleans and returns False when and only when the first is True and the second False. For those who want a challenge, make the body one line long.

Test Cases:

>>> implies(True, True)

True

>>> implies(True, False)

False

>>> implies(False, True)

True

>>> implies(False, False)

True

Neither Nor

Write a function named neither_nor that takes two Booleans and returns True when and only when  both are False. Challenge: make the body one line long.

Test Cases:

>>> neither_nor(True, True)

False

>>> neither_nor(True, False)

False

>>> neither_nor(False, True)

False

>>> neither_nor(False, False)

True

Sign Function

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

Sign Count

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!

Letter Grades

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

Letter Grades Plus

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'

Could It Be a Triangle?

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

Order Three

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)

Greatest, Least

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)

Acute, Right or Obtuse?

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

Digit Count

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

Leap Year

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

Newton's Method

Let's say we wish to approximate the square root of a number n. Here's a method:

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

Prime Test

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

The Hailstone Algorithm

Below is the so-called "Hailstone Algorithm". 

 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]

Least Common Multiple

Here's a pretty little algorithm that computes the least common multiple (LCM) of a pair of positive integers p and q.

 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

Greatest Common Factor

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.

 Write a function that implements this algorithm. Name it GCF.  Its inputs are two positive integers. Its output is their GCF.

A few tips:

Test Cases:

>>> GCF(2, 3)

1

>>> GCF(3, 3)

3

>>> GCF(30, 42)

6

Reduce a Fraction

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)

Add Fractions

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)

Triple Count (Stretch)

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