In-Class#

Coding Practice#

Code 5.1: Next Even Number#

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.

Look at the following code:

def next_even(n):
    return 0

test1 = next_even(5) == 6
test2 = next_even(-5) == -4
test3 = next_even(6) == 8
test4 = next_even(-6) == -4
test5 = next_even(0) == 2
test6 = next_even(-1) == 0

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

The code contains a placeholder function that always returns 0 and is clearly not correct. However, it includes good test cases for the function next_even. This is to show that you can write test cases before writing the function.

Now, write the function next_even so that it passes the tests.

Code 5.2: Instructions for the Next Two Exercises#

Writing tests for functions often involves just as much work as writing the functions themselves and is just as important. For the next two problems, we suggest that you find a buddy and test each other’s functions. Do the following:

  • Divide the tasks between you:

    • One student should write the function sign_text in the file sign_text.py and the test for number_digits in the file number_digits_test.py. This student should not create a solution for the number_digits function but may include a placeholder solution that always returns the same value.

    • The second student should write the function number_digits in the file number_digits.py and the test for sign_text in the file sign_text_test.py. This student should not create a solution for the sign_text function, but it is okay to place a placeholder solution that always returns the same value.

  • Send the files to each other, so that you both have all four files, and place these four files in the same folder.

  • Use two different testing strategies:

    • Test the sign_text function by placing the test code and the function definition in the same file and executing this file.

    • Test the number_digits function by importing the function into the test file and running the test file. That is, you should write

      from number_digits import number_digits
      

      in the first line of the test file. This will allow you to call the function number_digits in the test file.

  • If your functions do not pass the tests, debug them together. The bugs can be in the function or in the test.

  • You can use the same testing strategy for other exercises as well.

Code 5.3: Sign Text#

We often need to determine whether a number is positive, negative, or zero. Write a function sign_text that takes a number as input and returns a string 'positive' , 'negative' , or 'zero' based on the sign of the number.

The test for this function should contain at least six different numbers, including positive, negative, and zero, both integers and floats.

Code 5.4: Number of Digits#

We often need to determine the number of digits in an integer. 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: Customized Greeting#

We need a function customized_greeting that constructs a polite greeting depending on the time of day and customized to a specific person. The function should take two arguments: name and hour.

The argument name is a string containing the name of the person. The argument hour is the hour of the day given in 24-hour format, so it is an integer between 0 and 23.

The greeting should start with:

  • 'Good morning' for all hours between 7 and 11.

  • 'Good afternoon' for all hours between 12 and 17.

  • 'Good evening' for all hours between 18 and 22.

  • 'Good night' for all other values of hour.

The greeting should be followed by a comma, the name of the person, and an exclamation mark.

For example, customized_greeting('Alice', 8) should return 'Good morning, Alice!'.

Code 5.6: Disc Area and Sphere Surface Area#

The area of a disc is given by the formula \(A = \pi r^2\), and the surface area of a sphere is given by the formula \(S = 4 \pi r^2\).

Write two functions disc_area and sphere_surface_area that take the radius of a disc and a sphere as input and return the area of the disc and the surface area of the sphere, respectively. Use math.pi as the value of \(\pi\), and remember to import the math module. Can you write sphere_surface_area in a way such that it uses (calls) disc_area? Test your functions.

Code 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.

Code 5.8: Is Divisible#

We are often interested in knowing whether a number is divisible by another number. For example, 28 is divisible by 7, because \(28 = 7 \cdot 4\), but 29 is not divisible by 7 because we would get a remainder of 1.

Write a function called is_divisible that takes the numbers num and divisor as arguments and returns True if num is divisible by divisor.

Test your function on a range of values. For example, is_divisible(10, 2) should return True and is_divisible(10, 3) should return False.

Problem Solving#

Problem 5.9: Profit Margin#

The profit margin is a measure of how much of the company’s revenue is profit. 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_pct that receives two parameters, revenue and cost, 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: 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 concentration of the diluted solution will be \(\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.11: Deflection of a Beam#

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.

image

Write a function that calculates the deflection of a beam. The function should have the following signature:

calculate_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. Try some more interesting values and see if the function returns the expected result.

Problem 5.12: Wind Chill#

Wind chill is the lowering of body temperature due to the flow of cool air. The perceived temperature is denoted by 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 Celsius and \(v\) is the wind speed in km/h.

Write the function wind_chill that takes as input the actual temperature temperature (in degrees Celsius) and the wind speed windspeed (in km/h). The function should return the perceived temperature rounded to the nearest integer.

The code below can be used to nicely print the result.

windchill = wind_chill(temperature, windspeed)
print(round(temperature), "°C with a wind speed of",
            round(windspeed), "km/h feels like", windchill, "°C.")

To test your function, we have supplied test_wind_chill.py. You can run this file to see if your function is working as expected. The file runs the following four tests and checks if the output matches the expected outputs shown below.

>>> wind_chill(10, 25)
7
>>> wind_chill(0, 100)
-11
>>> wind_chill(40, 0)
38
>>> wind_chill(40, 20)
45

Problem 5.13: 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:

---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
Cell In[17], line 22
     18         hour = hour + 1
     20     return -1
---> 22 print_docstring(bacterial_growth)

File /builds/pgcs/programmering/pg_cs/utils/print_docstring.py:88, in print_docstring(func)
     87 def print_docstring(func):
---> 88     display_markdown(autofunction_md(func))

File /builds/pgcs/programmering/pg_cs/utils/print_docstring.py:49, in autofunction_md(func, wrap_div)
     47 def autofunction_md(func, wrap_div=True):
     48     doc = docstring_parser.parse(func.__doc__)
---> 49     perform_checks(func, doc)
     50     signature_args = "(" + ', '.join([f"{p.arg_name}" for p in doc.params]) + ")"
     51     signature = f"{func.__name__}{signature_args}"

File /builds/pgcs/programmering/pg_cs/utils/print_docstring.py:30, in perform_checks(func, doc)
     28 func_args = [arg.arg for arg in tree.body[0].args.args]
     29 if source != source_dedent:
---> 30     assert func_args[0] == 'self', f"First argument of method is not 'self', but {func_args[0]}."
     31     func_args = func_args[1:]
     32 # check if all arguments are in doc and have the same names

AssertionError: First argument of method is not 'self', but initial.

One expected output is shown below.

>>> bacterial_growth(100, 0.1, 1000)
44

You should also test your function with test_bacterial_growth.py.

Problem 5.14: Blood Pressure#

Measurement of blood pressure consists of two values: systolic blood pressure (a higher value) and diastolic blood pressure (a lower value). Based on these two values, blood pressure can be categorized as indicated in the chart below.

image

Create a function blood_pressure that takes two numbers, representing the systolic and diastolic blood pressure, as input. The function should return a string with the blood pressure category, written exactly as in the chart above. If a measurement is between categories, the category for the higher value should be returned.

For example, if the systolic blood pressure is 120 and the diastolic blood pressure is 65, according to the chart, the measurement is between the normal and prehigh blood pressure catergories. The function should return the higher category, which is the string 'Prehigh'.

The function should have the following specifications:

blood_pressure(systolic, diastolic)

Categorize blood pressure based on systolic and diastolic readings.

Parameters:

  • systolic

int

A number with a valid systolic blood pressure.

  • diastolic

int

A number with a valid diastolic blood pressure.

Returns:

  • str

A string with the blood pressure category.

The script solving this problem was presented in the live demo. You can now make a function based on it and test it with the following:

Hide tests
test0 = blood_pressure(120, 65) == 'Prehigh'
test1 = blood_pressure(100, 95) == 'Stage 1 high'
test2 = blood_pressure(80, 50) == 'Low'
test3 = blood_pressure(110, 70) == 'Normal'
test4 = blood_pressure(133, 82) == 'Prehigh'
test5 = blood_pressure(141, 99) == 'Stage 1 high'
test6 = blood_pressure(189, 101) == 'Stage 2 high'
test7 = blood_pressure(90, 60) == 'Normal'
test8 = blood_pressure(170, 80) == 'Stage 2 high'
test9 = blood_pressure(140, 50) == 'Stage 1 high'
test10 = blood_pressure(160, 60) == 'Stage 2 high'

all_tests_passed = test0 and test1 and test2 and test3 and test4 and test5 and test6 and test7 and test8 and test9 and test10 
print("All tests passed:", all_tests_passed)

Problem 5.15: Candy Exchange#

The owner of a candy shop devised a plan to reduce littering in front of the shop: “Bring back five pieces of candy wrapping, and get one candy for free!” This poses a question: If you have a certain number of candies, how many candies can you actually end up eating? Here we assume that you keep returning the wrappers as long as possible.

Create a function candy_exchange that takes the number of candies you have as input, and returns the number of candies you can end up eating.

For example, let’s say you start with 73 candies. After eating these candies, you are left with 73 wrappers. You have eaten 73 candies, and you have 73 wrappers. You exchange 70 wrappers for 14 new candies, which you eat. You have now eaten 87 candies, and you have 17 wrappers. Continuing this way, we can summarize the status in the table below, showing that you can eat 91 candies in total.

Candies eaten

73

87

90

91

Wrappers

73

17

5

1

The function should have the following specifications:

candy_exchange(n)

Simulates the exchange of wrappers for more candies.

Parameters:

  • n

int

The number of candies you have.

Returns:

  • int

The number of candies you can end up eating.

The script solving this problem was presented in the live demo. You can now make a function based on it and test it with the following:

Hide tests
test0 = candy_exchange(73) == 91
test1 = candy_exchange(12) == 14
test2 = candy_exchange(22) == 27
test3 = candy_exchange(91) == 113
test4 = candy_exchange(182) == 227
test5 = candy_exchange(16) == 19
test6 = candy_exchange(1) == 1
test7 = candy_exchange(5) == 6
test8 = candy_exchange(13) == 16
test9 = candy_exchange(36) == 44
test10 = candy_exchange(94) == 117

all_tests_passed = (test0 and test1 and test2 and test3 and test4 and test5
                         and test6 and test7 and test8 and test9 and test10)
print("All tests passed:", all_tests_passed)

Problem 5.16: Fibonacci Sequence Check#

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 fibonacci_check 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 fibonacci_check(5) should return 6. Here are some additional examples:

>>> fibonacci_check(5)
6
>>> fibonacci_check(14)
-1
>>> fibonacci_check(121393)
27
>>> fibonacci_check(14930352)
37

The function specifications are:

fibonacci_check(n)

Determines which Fibonacci number the input is.

Parameters:

  • n

positive int

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