Week 5: Functions

Week 5: In-Class#

Demo#

Demo 5.1: Fall Duration#

The distance traveled by an object falling from rest is given by the formula

\[s = \frac{1}{2}g t^2\, , \]

where \(s\) is the distance traveled (in meters), \(t\) is the duration of the fall (in seconds), and \(g\) is the gravitational acceleration on Earth, equal to \(9.81\mathrm{m}/\mathrm{s}^2\).

Pen & Paper

For heights of 100 m and 24 m, calculate the fall duration.

If you do not have a pen or paper at hand, you can type your answers here:

Height100 meters24 meters
Fall duration

Demo 5.2: Customized Greeting#

It is a custom to greet people differently depending on the time of day. We use a convention that morning starts at 4:30, and lasts until 12:00 when afternoon begins. At 17:30 evening starts, and night starts at 23:00. Morning, afternoon, evening, and night are marked with green, red, light blue, and dark blue colors, respectively, in the figure below.

Drawing of time periods

We want to determine a suitable greeting based on the time of day: Good morning!, Good afternoon!, Good evening!, or Good night!. Each time period includes its starting time but excludes its ending time. For example, 4:30 is morning, but 12:00 is afternoon.

Pen & Paper

According to the convention above, write down what the greeting should be for the following times of day: 7:00, 12:30, 18:15, 23:45, and 3:15. Also, determine the correct greeting for times just before and just after each boundary. Finally, notice that night includes times before midnight and after midnight. Write down the time for both these periods.

Coding Practice#

Code 5.3: Function Testing (Buddy-Exercise)#

Writing tests for functions often involves just as much work as writing the functions themselves and is just as important. You can write tests before writing the function. You can even write tests for a function that you do not yet know how to write. For example, suppose a function count_a should count how many times the lowercase letter a appears in a string. You have not yet learned how to write such a function, but you can already write tests which check, whether the function works as intended.

For this, you can create a dummy version of the function that always returns the same value, for example, 0. Then, you can write and run tests. Since the dummy version does not do what it is supposed to, the tests will fail. Once you have the correct function, it should pass the tests.

Below you can see a dummy version of the function count_a and some tests. Run it to see whether all tests pass or not.

def count_a(s):
    return 0

test1 = count_a("banana") == 3
test2 = count_a("apple") == 1  # starting with 'a'
test3 = count_a("drape") == 1  # containing 'a' 
test4 = count_a("milla") == 1  # ending with 'a'
test5 = count_a("cherry") == 0  # no 'a'
test6 = count_a("") == 0  # empty string
test7 = count_a("Aardvark") == 2  # case sensitivity
test8 = count_a("aaaaaa") == 6  # all 'a'
all_tests_passed = (test1 and test2 and test3 and test4 and test5 and test6 
        and test7 and test8)

print("All tests passed:", all_tests_passed)
if not all_tests_passed:
    print("Individual test results:", test1, test2, test3, test4, test5, test6, 
            test7, test8)

As you can see, a dummy version may pass some tests by chance, but it will fail others. This is why it is useful to test a function with several inputs that cover different situations. If a function passes all the tests, this is evidence that it may work correctly, but additional tests can give you more confidence.

For this buddy exercise, we will practice writing functions and tests using Code 5.4 (Number Digits) and Code 5.5 (Next Even). Read both exercises first. Then follow the instructions below with your buddy.

  1. Look at Code 5.4 and write the number_digits function in the number_digits.py file.

  2. Create the next_even_test.py file and write a dummy version of the next_even function in it. The dummy function should always return 0.

  3. Look at Code 5.5 and write tests for the next_even function in the next_even_test.py file.

  1. Look at Code 5.5 and write the next_even function in the next_even.py file.

  2. Create the number_digits_test.py file and write a dummy version of the number_digits function in it. The dummy function should always return an empty string.

  3. Look at Code 5.4 and write tests for the number_digits function in the number_digits_test.py file.

Now send the files to each other, so that you both have all four files, and place the four files in the same folder. Together, test both functions using the following two strategies.

Strategy 1: Put the function and its test in the same file

For each function, put the function and its test in the same file. You can either add the tests below the real function or copy the real function over the dummy function in the test file. Then run the file containing the function and its tests.

Strategy 2: Import the function into the test file

Instead of copying the real function into the test file, you can import it. This works because the function file and the test file are in the same folder. For example, to test next_even, add this line to next_even_test.py:

from next_even import next_even

Delete the dummy next_even function from the test file after adding the import, and then run the test file.

Use both strategies to test both functions. If a function does not pass the tests, debug the function and the tests together.

Code 5.4: Number Digits#

Write a function number_digits that takes a positive integer as input and returns the number of digits in the number.

The test for this function should contain at least six different positive integers with varying numbers of digits.

Code 5.5: Next Even#

You should write a function next_even that takes an integer n as input and returns the next even number. That is, if n is even, the function should return the even number that is 2 greater than n. If n is odd, the function should return the even number that is 1 greater than n. The function should work for both positive and negative integers.

The test for this function should contain numbers that are positive, negative, even, and odd. Also include a test for the number 0.

Code 5.6: Cylinder Volume#

The volume of a cylinder is given by the formula \(V = A h\), where \(A\) is the area of the base of the cylinder, and \(h\) is the height of the cylinder. The area of the base is given by the formula \(A = \pi r^2\), where \(r\) is the radius of the base.

Write first the function disc_area that takes the radius of a disc as input and returns the area of the disc. Use math.pi as the value of \(\pi\), and remember to import the math module. Test your function.

Now, in the same file, write the function cylinder_volume that takes the radius and height of a cylinder as input and returns the volume of the cylinder. Can you write cylinder_volume in a way such that it uses (calls) disc_area?

Here, disc_area is a helper function for cylinder_volume. A helper function may be placed in the same file as the main function, or in a separate file that is imported similar to how you imported tests earlier today.

Problem Solving#

Problem 5.7: Parts to Ratio#

When mixing liquids, the mixing instructions are often given in parts. For example, instructions to mix elderflower syrup with water might say 1 + 4, which means that you should mix one part elderflower syrup with four parts water. Given such instructions, we are interested in knowing the ratio of the one part in relation to the total liquid. Again looking at the example 1+4, the obtained mixture has \(1 + 4 = 5\) parts in total, and the elderflower syrup is one part of this. Therefore, the ratio of elderflower syrup to the total liquid is \(\frac{1}{5} = 0.2\).

Write a function parts_to_ratio that takes two arguments, part and part_other, and returns the ratio of part in relation to the total liquid.

Problem 5.8: Dilute Solution#

We want to dilute a solution by mixing it with a solvent. The mixing proportions are given in parts. The solution has a certain concentration, and after mixing it with the solvent, the concentration will be lowered. We want to compute the new concentration of the solution after it is mixed.

For example, consider having a solution that has a concentration of \(0.5\) g/L and mixing 2 parts of solution with 3 parts solvent. The resulting solution has \( 2 + 3 = 5 \) parts, of which 2 parts are the original solution. Therefore, the resulting concentration after dilution is \(\frac{2}{5}\cdot 0.5 = 0.2\) g/L.

Write a function dilute with arguments concentration, part_solution, and part_solvent that returns the concentration of the diluted solution.

Problem 5.9: Profit Margin#

Profit margin measures the percentage of revenue that remains as profit after costs. The profit margin is calculated as

\[ \frac{r - c}{r} \]

where \(r\) is the revenue and \(c\) is the cost.

Create a function called profit_margin that receives two parameters, cost and revenue, and returns the profit margin as a percentage. The following companies would like to know how much profit margin they have made in the last year. Use the function to calculate the profit margin for each company. We use 10**9 for a billion.

Company

Cost ($)

Revenue ($)

Profit Margin (%)

Lemonade stand inc.

20

40

50

Floormart

30 billion

31 billion

Ztartup

200000

144000

Orange Inc.

60 billion

83 billion

Problem 5.10: Beam Deflection#

The deflection of a beam is given by

\[ D = \frac{M L^3}{3 \lambda I} \]

where \(D\) is the deflection, \(M\) is the load, \(L\) is the length of the beam, \(\lambda\) is the modulus of elasticity, and \(I\) is the moment of inertia. The illustration below shows an unloaded and a loaded beam.

Beam deflection drawing

Write a function that calculates the deflection of a beam. The function requirements are:

beam_deflection.py

beam_deflection(length, load, modulus_of_elasticity, moment_of_inertia)

Calculate the deflection of a beam under a load.

Parameters:

  • length

float

The length of the beam (m).

  • load

float

The load applied to the beam (N).

  • modulus_of_elasticity

float

The modulus of elasticity of the beam material (Pa).

  • moment_of_inertia

float

The moment of inertia of the beam’s cross-section (m4).

Returns:

  • float

The deflection of the beam (m).

How to test this function? Consider first setting all parameters to 1 and calculate the expected result by hand. See if your function returns the same result.

Now try some more realistic values: a load of 500 N (approximately 51 kg applied) applied to a 2 m long steel beam with a rectangular 5 cm x 10 cm cross section. Steel has a modulus of elasticity of \(200 \times 10^9\) Pa, and the moment of inertia for this profile is \(I = \frac{bh^3}{12} = \frac{0.05 \cdot 0.1^3}{12} \approx 4.1667 \cdot 10^{-6}\ \mathrm{m}^4\).

Your function should give a deflection of approximately 1.6 mm.

Tip

To avoid spelling mistakes or errors in capitalization and punctuation, copy the text from this page and paste it into your code.

Problem 5.11: Wind Chill#

Wind chill is the lowering of body temperature due to the flow of cool air. The perceived temperature is the wind chill temperature \(T_{WC}\). The cooling effect depends on air temperature \(T\) and wind speed \(v\) with the formula:

\[ T_{WC} = 13.12 + 0.6215 T - 11.37 v^{0.16} + 0.3965 T v^{0.16} \]

where \(T\) is the temperature in degrees Celsius and \(v\) is the wind speed in km/h.

Write a function that takes as input the actual temperature (in degrees Celsius) and the wind speed (in km/h). The function should return a string in the format X degrees with Y km/h wind feels like Z degrees., where X is the actual temperature rounded to the nearest integer, Y is the wind speed rounded to the nearest integer, and Z is the perceived temperature rounded to the nearest integer.

The expected behavior of the function is shown below.

>>> wind_chill(-3.6, 25.6)
'-4 degrees with 26 km/h wind feels like -11 degrees.'

The function requirements are:

wind_chill.py

wind_chill(temperature, windspeed)

Calculate the wind chill index based on the temperature and wind speed.

Parameters:

  • temperature

float

The actual temperature in degrees Celsius.

  • windspeed

float

The wind speed in km/h.

Returns:

  • str

A string describing the wind chill effect.

Above, you can see how we specify the requirements for functions in this course. As explained in preparation, we show expected behavior in interactive mode, which you can recognize by the >>> prompt. Function requirements are shown in a box with the name of the file in the frame above the box.

To test your wind_chill function, we have provided test code in the file test_wind_chill.py. The test code imports your function and checks whether it works as expected, just as you practiced in the Buddy Exercise in Code 5.3. Download the file and place it in the same folder as your function file; otherwise, the import will not work.

Run the test file to check your function. It runs four tests and reports the expected output and your function’s output when a test fails.

Problem 5.12: Bacterial Growth#

We grow bacteria in a closed container that can sustain a certain number of bacteria. To investigate bacterial growth, we use the model

\[ \Delta N = r N \left(\frac{K - N}{K}\right) \]

where \(\Delta N\) is the change in the number of bacteria from one hour to another, \(r\) is the hourly growth rate, \(N\) is the number of bacteria, and \(K\) is the maximum number of bacteria that can be in the container. The expression in the parentheses is the fraction of the container that is empty. So if no space is empty and \(N = K\) the number of bacteria will not increase further.

Starting from an initial population, we want to know how many hours it will take for the bacteria to fill more than 90% of the container.

Consider \(N=100\), \(r=0.1\), and \(K=1000\). After one hour, the population will be 109, then approximately 118.7, 129.2, 140.4, and so on. The population will reach 90% of the maximum population after 44 hours.

Create a file called bacterial_growth.py. In this file create a function with the following specifications:

bacterial_growth.py

bacterial_growth(initial, growth_rate, max_bact)

Simulates bacterial growth and returns the time taken to reach 90% of the maximum population.

Parameters:

  • initial

float

The initial number of bacteria.

  • growth_rate

float

The growth rate of the bacteria.

  • max_bact

float

The maximum population of the bacteria.

Returns:

  • int

The number of hours for the bacteria to reach 90% of the maximum population.

Returns -1 if the population does not reach 90% within one week.

One expected output is shown below.

>>> bacterial_growth(100.0, 0.1, 1000.0)
44

To test your function, we have provided test code in the file test_bacterial_growth.py. This file contains only one test, so you may wonder whether it is worth using. However, it can catch simple mistakes such as misspelling the filename or the function name, so there is still value in using it.

Problem 5.13: Which Fibonacci?#

The Fibonacci sequence is a series of numbers where each number is the sum of the two preceding ones. The sequence starts as follows:

\[ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, \ldots \]

Create a function which_fibonacci that takes an integer as input, and returns which position this integer has in the list of fibonacci numbers. If the number is not in the list, the function should return -1.

To avoid ambiguities, you can assume the input is 2 or greater.

For example, the number 5 is the 6th fibonacci number, so which_fibonacci(5) should return 6. Here are some additional examples:

>>> which_fibonacci(5)
6
>>> which_fibonacci(14)
-1
>>> which_fibonacci(14930352)
37

The function specifications are:

which_fibonacci.py

which_fibonacci(n)

Determines which Fibonacci number the input is.

Parameters:

  • n

int, positive

The number we wish to test.

Returns:

  • int

The position in the list of Fibonacci numbers. If it is not in the list, return -1.

Solutions to Demo Tasks