CoCalc Public Filesassignment answers / Assignment 3 / Assignment3.ipynbOpen with one click!
Author: Alexander Hafner
Views : 77
Compute Environment: Ubuntu 20.04 (Default)
Readonly, ID: c5918e

Assignment 3

This is an ungraded assignment!

Deadline: 8th Oct - 15:00 - After this time, don't change your notebook. It'll be automatically collected by CoCalc.

Readonly, ID: 4c0075

Advanced debugging with f-strings

Python 3.6 and later have something called f-strings. They are very useful for debugging. To see if we can use them first we check which Python version we are using.

Readonly, ID: 6ac8df
In [1]:
import sys print(sys.version)
3.8.2 (default, Jul 16 2020, 14:00:26) [GCC 9.3.0]
Readonly, ID: c4ddb5

The syntax is simple. Just put an 'f' in front of your string to turn it into an f-string. In an f-string anything that is inside curly braces will be evaluated as Python code. The value in the brackets will then be replaced by the value of your expression. The cell below shows an example.

Readonly, ID: 79eae9
In [4]:
import sys version = sys.version print("Python version is"":", version) # what you already know print("-----------------------------") print(f"Python version is: {version}") # using f-strings
Python version is: 3.8.5 (default, Jul 28 2020, 12:59:40) [GCC 9.3.0] ----------------------------- Python version is: 3.8.5 (default, Jul 28 2020, 12:59:40) [GCC 9.3.0]
Readonly, ID: task1

Task 1 - for-loop (1 Point)

Write a for-loop that creates a string containing all uneven numbers from 1 to 100 without spaces (you start at 1, so 1 should be included). Store this string in the variable solution1.

HINT: Use range().

In [9]:
solution1 = '' for i in range(1, 100, 2): solution1 += str(i) print(f'The solution is: {solution1}')
The solution is: 13579111315171921232527293133353739414345474951535557596163656769717375777981838587899193959799
Answer that will be automatically graded below, ID: task1_answer
In [10]:
solution1 = "" ### BEGIN SOLUTION for i in range(1, 100, 2): solution1 += str(i) ### END SOLUTION
Test your code from above here(1 point), ID: task1_tests
In [11]:
from nose.tools import assert_equal, assert_true, assert_false # Checks correct datatype assert_true(isinstance(solution1, str), msg="Your solution is not a string.") # Checks correct beginning assert_false(solution1[0] == "0", msg="You should start at 1 (one), not 0 (zero).") assert_true(solution1[0] == "1", msg="You should start at 1 (one).") # Checks correct solution assert_equal(solution1, "13579111315171921232527293133353739414345474951535557596163656769717375777981838587899193959799", msg="Your solution does not match the correct solution.") print("\x1b[6;30;42mSuccess!\x1b[0m")
Success!
Readonly, ID: task2

Task 2 Advanced for-loop and range() (2 Points)

Write a for-loop that creates a string containing the digit sum of every third number from 1 to 1000 without spaces (you start at the number 1). Store this string in the variable solution2.

To calculate the digit sum you sum up all the digits in a number.

An example for the numbers from 1 to 13 is given below:

Number Digit Sum
1 1
4 4
7 7
10 1 + 0 = 1
13 1 + 3 = 4

The solution here is the string "14714". You should not calculate the digit sum of the string containing every third number from 1 to 1000. For every third number, calculate the digit sum, then add it to solution2 as a string.

Don't confuse it with the digital root. The digital sum of 99 is 18, the digital root of 99 is 9. You obtain the digital root by calculating the digital sum until you reach a single digit number.

HINT: Use range().

HINT: Iterate over the digits of each number inside the for-loop with another for-loop.

In [16]:
solution2 = '' for i in range(1, 1000, 3): output = 0 for digit in str(i): output += int(digit) solution2 += str(output)
Answer that will be automatically graded below, ID: task2_answer
In [18]:
solution2 = "" ### BEGIN SOLUTION for i in range(1, 1000, 3): # solution as expected by the students # this should be the easiest way to solve the task for them out = 0 for digit in str(i): out += int(digit) solution2 += str(out) ### END SOLUTION
Test your code from above here(1 point), ID: task2_tests
In [6]:
from nose.tools import assert_equal, assert_true, assert_false # Checks correct datatype assert_true(isinstance(solution2, str), msg="Your solution is not a string.") # Checks correct beginning assert_false(solution2[0] == "0", msg="You should start at 1 (one), not 0 (zero).") assert_true(solution2[0] == "1", msg="You should start at 1 (one).") # Checks correct solution assert_equal(solution2, "147147104710471047101371013710137101316101316101316147104710471047101371013710137101316101316101316101316194710471047101371013710137101316101316101316101316191316194710471013710137101371013161013161013161013161913161913161947101371013710137101316101316101316101316191316191316191316192271013710137101316101316101316101316191316191316191316192216192271013710131610131610131610131619131619131619131619221619221619227101316101316101316101316191316191316191316192216192216192216192225101316101316101316191316191316191316192216192216192216192225192225101316101316191316191316191316192216192216192216192225192225192225", msg="Your solution does not match the correct solution. Did you store your solution in the variable 'solution1' instead of 'solution2'? You should use 'solution2' for this task.") print("\x1b[6;30;42mSuccess!\x1b[0m")
Success!
Readonly, ID: task3

Task 3 - prime checking (2 Points):

Write a function is_prime that takes a positive integer as argument and returns True if the integer is a prime number and False if it is not.

HINT: To check if a number is dividable by another number you can use modulo (%).

HINT: Don't overcomplicate things: Check if the number your function gets as an argument is dividable by another number (other than 1 and itself) and if it is not then you have found a prime number.

Answer that will be automatically graded below, ID: task3_answer
In [7]:
### BEGIN SOLUTION import math def is_prime(num): if num == 2: return True if num < 2 or num % 2 == 0: return False # can be solved without the sqrt, just makes it faster for n in range(3, int(math.sqrt(num)) + 1, 2): if num % n == 0: return False return True ### END SOLUTION
Test your code from above here(0.5 points), ID: task3_tests_checkSimple
In [8]:
from nose.tools import assert_equal, assert_true assert_true("is_prime" in locals(), msg="The function 'is_prime' does not exist. Did you missspell the name?") assert_true(is_prime(2), msg="2 is a prime number.") print("\x1b[6;30;42mSuccess!\x1b[0m")
Success!
Test your code from above here(1.5 points), ID: task3_tests_checkComplex
In [9]:
from nose.tools import assert_equal, assert_true assert_true(is_prime(2029)) assert_true(is_prime(82261)) assert_true(is_prime(181361)) assert_true(is_prime(226169)) assert_false(is_prime(1)) assert_false(is_prime(4)) assert_false(is_prime(42)) assert_false(is_prime(43249329423842390572358290)) print("\x1b[6;30;42mSuccess!\x1b[0m")
Success!
Readonly, ID: task4

Task 4 - function with default parameters (1 Point)

Define a function sum_numbers that takes three arguments: start, end and step. It should sum up all numbers between start (inclusive) and end (exclusive), with a step size of step and return this sum.

Example: start = 1, end = 10, step = 2 should sum up every second number between 1 and 10.

1 + 3 + 5 + 7 + 9 = 25

The parameters should be optional with default values as in the example above.

HINT: You can use sum() to sum up all values in an iterable (such as a list, or a tuple, ...).

Answer that will be automatically graded below, ID: task4_answer
In [10]:
### BEGIN SOLUTION def sum_numbers(start=1, end=10, step=2): return sum(range(start, end, step)) ### END SOLUTION
In [21]:
a = [1, 2, 3, 4, 5] a
[1, 2, 3, 4, 5]
In [22]:
sum(a)
15
In [23]:
sum(range(4))
6
Test your code from above here(0.25 points), ID: task4_tests_checkReturn
In [11]:
from nose.tools import assert_false assert_false(sum_numbers() is None, msg="Your function returns None. Did you forget a 'return' statement?") print("\x1b[6;30;42mSuccess!\x1b[0m")
Success!
Test your code from above here(0.25 points), ID: task4_tests_checkSignature
In [12]:
from nose.tools import assert_true, assert_equal from inspect import signature s = signature(sum_numbers) assert_true("start" in s.parameters, msg="The 'start' parameter is missing.") assert_true("end" in s.parameters, msg="The 'end' parameter is missing.") assert_true("step" in s.parameters, msg="The 'step' parameter is missing.") assert_equal(s.parameters["start"].default, 1, msg="The default value of the 'start' parameter is wrong, it should be 1.") assert_equal(s.parameters["end"].default, 10, msg="The default value of the 'end' parameter is wrong, it should be 10.") assert_equal(s.parameters["step"].default, 2, msg="The default value of the 'step' parameter is wrong, it should be 2.") print("\x1b[6;30;42mSuccess!\x1b[0m")
Success!
Test your code from above here(0.5 points), ID: task4_tests_checkFunctionality
In [13]:
from nose.tools import assert_equal, assert_false assert_equal(sum_numbers(1, 10, 2), 25, msg="The order of your function parameters seems to be wrong.") assert_equal(sum_numbers(2, 10, 2), 20, msg="Your function returns an incorrect result when called with a different 'start' value. Are you using the function arguments in your calculation?") assert_equal(sum_numbers(1, 100, 2), 2500, msg="Your function returns an incorrect result when called with a different 'end' value. Are you using the function arguments in your calculation?") assert_equal(sum_numbers(1, 10, 3), 12, msg="Your function returns an incorrect result when called with a different 'step' value. Are you using the function arguments in your calculation?") assert_equal(sum_numbers(), 25, msg="Incorrect result when using only the default values. Did you forget to define the default values?") assert_equal(sum_numbers(42, 92832, 17), 253488665, msg="Your function returns an incorrect result.") print("\x1b[6;30;42mSuccess!\x1b[0m")
Success!
Readonly, ID: task5

Task 5 - FizzBuzz (5 Points)

Write a function fizzbuzz with one argument limit. Your function should return a list with limit + 1 items (so if limit = 10 your list should have length 11 - remember that lists start counting at 0!) where for each index the value in the list should be "Fizz" if the index is a multiple of three, "Buzz" if the index is a multiple of five and in case it is a multiple of both three and five it should contain "FizzBuzz", in all other cases it should be the index itself.

Example:

fizzbuzz(15) should return a list with 16 elements (this means it goes up to lst[15]).

  • 2 is not a multiple of three or five, so lst[2] == 2 (the integer 2, not the string '2')
  • 3 is a multiple of three, so lst[3] == "Fizz"
  • 15 is a multiple of three and five, so lst[15] == "FizzBuzz"

HINT: Be careful when using if and elif, as a number can be a multiple of both three and five!

HINT: Don't forget to handle the case when a number is not a multiple of three or five.

Answer that will be automatically graded below, ID: task5_answer
In [24]:
### BEGIN SOLUTION def fizzbuzz(limit): lst = [""] * (limit + 1) for i in range(limit + 1): flag = False if i % 3 == 0: lst[i] += "Fizz" flag = True if i % 5 == 0: #deshalb verwenden wir hier if anstatt elif --> dieses if kann auch True sein, wenn erstes if schon True ist, wenn man elif verwenden würde, dann hört es beim ersten if true schon auf lst[i] += "Buzz" flag = True if not flag: lst[i] = i return lst fizzbuzz(15) ### END SOLUTION
['FizzBuzz', 1, 2, 'Fizz', 4, 'Buzz', 'Fizz', 7, 8, 'Fizz', 'Buzz', 11, 'Fizz', 13, 14, 'FizzBuzz']
Test your code from above here(1 point), ID: task5_tests_checkFunction
In [15]:
# These tests are worth 1 point from inspect import signature from nose.tools import assert_true assert_true("fizzbuzz" in locals(), msg="The function 'fizzbuzz' does not exist. Did you name your function correctly?") assert_true("limit" in signature(fizzbuzz).parameters, msg="The function 'fizzbuzz' does not have a parameter called 'limit'. Did you name it correctly?") assert_true(fizzbuzz(5) is not None, msg="The function 'fizzbuzz' does not return any value (it returns None!). Did you forget the 'return' statement?") assert_true(isinstance(fizzbuzz(5), list), msg="The function 'fizzbuzz' does not return a list.") print("\x1b[6;30;42mSuccess!\x1b[0m")
Success!
Test your code from above here(1 point), ID: task5_tests_checkCorrectLength
In [16]:
# These tests are worth 1 point from nose.tools import assert_true assert_true(len(fizzbuzz(5)) == 6, msg="The list your 'fizzbuzz' function returns does not have the correct length. The length should be equal to 'limit + 1'.") assert_true(len(fizzbuzz(100)) == 101, msg="The list your 'fizzbuzz' function returns does not have the correct length. The length should be equal to 'limit + 1'.") print("\x1b[6;30;42mSuccess!\x1b[0m")
Success!
Test your code from above here(1 point), ID: task5_tests_checkIntegerType
In [17]:
# These tests are worth 1 point from nose.tools import assert_true assert_true(isinstance(fizzbuzz(5)[1], int), msg="The list that your 'fizzbuzz' function returns should have integers as values for indices where it does not have 'Fizz', 'Buzz' or 'FizzBuzz'. Did you convert the value to string instead of keeping it as integer maybe? For this example when doing 'lst[1] == 1' should be True.") print("\x1b[6;30;42mSuccess!\x1b[0m")
Success!
Test your code from above here(2 points), ID: task5_tests_checkCorrectness
In [18]:
# These tests are worth 2 points from nose.tools import assert_true result = fizzbuzz(100) assert_true(result[0] == "FizzBuzz", msg="The value for '0' should be 'FizzBuzz'. '0' (zero) is divisible by both 3 and 5 (in Python at least).") assert_true(result[3] == "Fizz", msg="The value for '3' should be 'Fizz'.") assert_true(result[5] == "Buzz", msg="The value for '5' should be 'Buzz'.") assert_true(result[15] == "FizzBuzz", msg="The value for '15' should be 'FizzBuzz'.") assert_true(result[90] == "FizzBuzz", msg="The value for '90' should be 'FizzBuzz'.") assert_true(result[93] == "Fizz", msg="The value for '93' should be 'FizzBuzz'.") assert_true(result[95] == "Buzz", msg="The value for '95' should be 'Buzz'.") assert_true(result[97] == 97, msg="The value for '97' should be '97' (integer, not string).") print("\x1b[6;30;42mSuccess!\x1b[0m")
Success!
Readonly, ID: task6

Task 6 - function interaction (3 Point)

Write a function unique_elements that has one argument elems and returns a sorted list containing the the elements from elems without duplicates. If a value appears multiple times in elems you should return a list that contains only one copy of it. In other words: Remove the duplicates from elems.

Write a second function tuple_firsts that has one argument tpls. This argument will be a list of tuples. Your function should then call your unique_elements functions with a list that contains only the first elements from tpls and returns the value that is returned by unique_elements.

For the list of elements given above in the get_testing_elements() function this means that:

  • your tuple_firsts function is called with the list of tuples from _elements()
  • inside your tuple_firsts function you extract the first values and store them in a list: ["django", "django", "pandas", "pandas", "NumPy"]
  • you call your unique_elements function with the list from the previous step
  • unique_elements returns a (sorted) list containing the values ["django", "pandas", "NumPy"]
  • your tuple_firsts functions returns the value that unique_elements returns when you call it

HINT: Start with the unique_elements function! Then, when you think you have implemented the function correctly, try calling it with lists that contain duplicates and check if it works!

HINT: For the tuple_firsts function first find a way to get the first element from a list of tuples. Again, try calling your function and check if it works by using print() statements. Once you are sure this works you can call your unique_elements function from inside your tuple_firsts function!

Readonly, ID: task6_elements
In [19]:
def get_testing_elements(): """ This function is here so you can use it for testing while minimizing the risk that you overwrite the values. Call this function and store it in a variable and then you can use that variable to test your functions. This is NOT required for the task, this is just here to help you test your solution. """ return [ ("django", "Python framework"), ("django", "(Unchained), movie"), ("pandas", "Cute animal 🐼"), ("pandas", "Python library"), ("NumPy", "Python library") ]
Answer that will be automatically graded below, ID: task6_answer
In [27]:
### BEGIN SOLUTION def unique_elements(elems): return sorted(list(set(elems))) #set for duplicate elimination def tuple_firsts(tpls): elems = [] for elem in tpls: elems.append(elem[0]) unique = unique_elements(elems) #returns sorted list return unique ### END SOLUTION
In [22]:
unique_elements([1, 2, 3, 3, 4, 4, 5, 69]) #eliminates one time 3 and one time 4
[1, 2, 3, 4, 5, 69]
In [26]:
tuple_firsts([(1, 2, 3), (34, 2, 3)]) #gets the first elements from a list of tuples
[1, 34]
Test your code from above here(2 points), ID: task6_tests_checkCorrectTupleFirstsness
In [22]:
from nose.tools import assert_equal test = [ ("hello", "world"), ("hello", "world"), ("foo", "bar") ] assert_equal(tuple_firsts(test), sorted(["hello", "foo"]), msg="Your 'tuple_firsts' function does not return the correct value.") print("\x1b[6;30;42mSuccess!\x1b[0m")
Success!