M04 Exercises¶

Functions¶

Exercise 4.1¶

Write a function with these requirements:

  • has a sensible name
  • contains a docstring
  • takes two inputs: a string and an integer
  • returns True if the string length is equal to the integer, else False

Call the function, passing inputs:

  • "is this text the right length?" for the string
  • 30 for the integer

Verify the output is True. Try other combinations.

In [69]:
def verify_string_length(text, val):
    """
    PURPOSE: check if the length of text is equal to a value
    
    INPUTS:
    text   str
    val    integer
    
    OUTPUTS:
    check  bool
    """
    
#     check = len(text) == val
#     return check
    return len(text) == val # Could just do this
In [72]:
txt = "is this text the right length?"
val = 30
out = verify_string_length(txt, val)
out
Out[72]:
True
In [73]:
verify_string_length("The foul occurred after the buzzer.", 10)
Out[73]:
False

Exercise 4.2¶

Write a function with these requirements:

  • takes *args for the input argument
  • squares each argument, printing the value. You can use a for loop.
  • returns None

Call the function, passing at least two integers.

In [159]:
def square_args(*vars):
    for var in vars:
        print(var**2)
    return None
In [157]:
square_args(2,3,8)
4
9
64
In [77]:
list_o_nums = [10, 100, 5, 62, 42]
square_args(*list_o_nums)
100
10000
25
3844
1764
In [160]:
square_args(10, 100, 5, 62, 42)
100
10000
25
3844
1764

Exercise 4.3¶

Write a function called word_shortener() that takes a long word and replaces it with a string that has the same first and last letters capitalized, and replaces the middle letters with the number of middle letters.

For example, "internationalization" would be returned as "I18N".

Make sure it only accepts strings, and of a minimum length.

Include a short docstring.

Hint: You can use isinstance() to check the variable type.

In [161]:
def word_shortener(long_word, min_len=5):
    "Converts a long word into word with internal letters replace by their count."
    
    # Validate the input
    if not isinstance(long_word, str):
        print(f"{long_word} is a {type(long_word)}, not a string!")
        return False
    elif len(long_word) < min_len:
        print(f"{long_word} is too short. Put in a word at least {min_len} letters long.")
        return False
    else:
        # Do the work ...
        first_letter = long_word[0].upper()
        last_letter = long_word[-1].upper()
        middle_number = len(long_word[1:-1])
        new_word = first_letter + str(middle_number) + last_letter
        return new_word
    
In [162]:
word_shortener('localization')
Out[162]:
'L10N'
In [80]:
get_shorty = lambda x: f"{x[0]}{len(x[1:-1])}{x[-1]}".upper()
In [81]:
get_shorty("operationalization")
Out[81]:
'O16N'

Exercise 4.4¶

Define a function with these requirements:

  • take one numeric value as input
  • subtract 5 from the value
  • return 1 if the difference is nonnegative, else return 0

Call the function on different values to test it.

In [5]:
def step(x):
    diff = x - 5
    if diff >= 0:
        return 1
    return 0
In [6]:
def step2(x):
    return 1 if x - 5 >= 0 else 0
In [8]:
def step3(x):
    return int(x - 5 >= 0)
In [9]:
step3(6.5)
Out[9]:
1
In [23]:
step(4)
Out[23]:
0

Scope¶

Exercise 4.5¶

Define a function that defines and prints a variable.

Show that calling this variable outside the function produces an error.

In [163]:
def myfunct():
    secret_x = 5
    print(secret_x)
In [164]:
myfunct()
5
In [165]:
print(secret_x)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[165], line 1
----> 1 print(secret_x)

NameError: name 'secret_x' is not defined

Exercise 4.6¶

Read the following code blocks (to be displayed).

For each block predict what the output will be.

def print_me():
    print(x1)

print_me()
In [86]:
def print_me():
    print(x1)

print_me()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[86], line 4
      1 def print_me():
      2     print(x1)
----> 4 print_me()

Cell In[86], line 2, in print_me()
      1 def print_me():
----> 2     print(x1)

NameError: name 'x1' is not defined
def print_me2():
    x2 = 5
    print(x2)

print_me2()
In [29]:
def print_me2():
    x2 = 5
    print(x2)
    
print_me2()
5
x2 = 7
print_me2()
In [30]:
x2 = 7
print_me2()
5
x2 = 7
print_me2()
print(x2)
In [31]:
x2 = 7
print_me2()
print(x2)
5
7
def print_me3(x3):
    print(x3)

x3 = 9
print_me3()
print_me3(x3)
print_me3()
print(x3)
In [32]:
def print_me3(x3):
    print(x3)
    
x3 = 9
print_me3()
print_me3(x3)
print_me3()
print(x)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[32], line 5
      2     print(x3)
      4 x3 = 9
----> 5 print_me3()
      6 print_me3(x3)
      7 print_me3()

TypeError: print_me3() missing 1 required positional argument: 'x3'
def print_me4(x4=5):
    print(x4)

x4 = 9
print_me4()
print_me4(x4)
print_me4()
print(x4)
In [33]:
def print_me4(x4=5):
    print(x4)
    
x4 = 9
print_me4()
print_me4(x4)
print_me4()
print(x4)
5
9
5
9

Lambda Functions¶

Exercise 4.6¶

Write a function that uses a lambda function to check if a value is in a range: $x >= 10$ and $x <= 20$

Call the function with different values to verify it works.

In [36]:
in_range = lambda x: x >= 10 and x <= 25
In [37]:
in_range(15)
Out[37]:
True
In [38]:
in_range(0)
Out[38]:
False

Exercise 4.7¶

Modify the lambda function called in_range to take three inputs x, a, b which are the value, lower bound, and upper bound, respectively.

Call the function with different x, a, b to verify it works.

In [87]:
in_range_general = lambda x, a, b: x >= a and x <= b
In [88]:
in_range_general(0, 1, 10)
Out[88]:
False
In [89]:
in_range_general(0, -1, 100)
Out[89]:
True

Exercise 4.8¶

Following the earlier example, write a lambda function called str_test that checks if a given string is found in another string, returning True if found, otherwise False.

In other words, check on a string is a substring of another.

Call the function with different inputs to verify it works.

In [43]:
str_test = lambda s1, s2: s2 in s1 
In [44]:
str_test("Opera is fun", "is")
Out[44]:
True
In [45]:
str_test("Opera is fun", "gasket")
Out[45]:
False

Exercise 4.9¶

  • Write three lambda functions:
    • c2f that converts a Celsius (C) value to Farenheit (F), given $F = 1.8C + 32$.
    • f2c that converts an F value to C, given $C = (F - 32)/1.8$
    • t2t that combines the two previous functions and takes two arguments:
      • an integer representing the temperature in either F or C.
      • a character representing the temparture scale to convert to, either 'f' or 'c'.
  • In addition:
    • Have t2t return the converted temperature as an integer by rounding the result.
    • AND instead of using an if statement in t2t, have it use a dictionary that contains elements for the two converter functions, i.e. c2f and f2c.

For example, if have a temperature of $45$ C and want to know what this is in F, it should work like this:

>>> f = t2t(45, 'f')
>>> print(f)
>>> 113
In [90]:
c2f = lambda cval: 1.8 * cval + 32
In [92]:
c2f(45)
Out[92]:
113.0
In [93]:
f2c = lambda fval: (fval - 32) / 1.8
In [94]:
f2c(113)
Out[94]:
45.0
In [95]:
targets = {
    'f': c2f,
    'c': f2c
}
In [96]:
t2t = lambda tval, target: round(targets[target](tval))
In [97]:
f = t2t(45, 'f')
In [98]:
print(f)
113

Or, more compactly:

In [54]:
targets = {
    'f': lambda cval: 1.8 * cval + 32,
    'c': lambda fval: (fval - 32) / 1.8
}
t2t = lambda tval, target: round(targets[target](tval))
In [55]:
t2t(45, 'f')
Out[55]:
113

The most compact

In [56]:
(lambda tval, target: round({'f': lambda cval: 1.8 * cval + 32, 'c': lambda fval: (fval - 32) / 1.8}[target](tval)))(45, 'f')
Out[56]:
113

Recursion¶

Exercise 4.10¶

Use this pattern to generate the next 4 terms (by hand)

Fib(2) = Fib(1) + Fib(0) = 1 + 0 = 1 
Fib(3) = Fib(2) + Fib(1) = 1 + 1 = 2  
Fib(4) = Fib(3) + Fib(2) = 2 + 1 = 3  
Fib(5) = Fib(4) + Fib(3) = 3 + 2 = 5

Now, write a Python function fibonacci() to return the nth term in the sequence.

Specifically, write the function with these requirements:

  • takes an integer n as input
  • includes the rules that defines the sequence
  • computes the nth term in the sequence, using recursion (the function will call itself)
  • returns the computed term

Call fibonacci(n) for $n = 0, 1, 2, 3, 4, 5$ and verify it works properly.

In [138]:
def fibonacci(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fibonacci(n-1) + fibonacci(n-2)
In [139]:
print(fibonacci(0))
print(fibonacci(1))
print(fibonacci(2))
print(fibonacci(3))
print(fibonacci(4))
print(fibonacci(5))
0
1
1
2
3
5
In [140]:
for n in range(6): print(fibonacci(n))
0
1
1
2
3
5

Think about how this works ... it's very cool!

If you call fibonacci(2),

  • flow goes to the else statement,
  • which calls fibonacci(1) and fibonacci(0),
  • so those need to get computed,

    • fibonacci(1) goes to the elif, returning 1
    • fibonacci(0) goes to the if, returning 0
    • back in the else statement, fibonacci(1) and fibonacci(0) => 1 + 0 = 1

    If you call fibonacci(3), a similar pattern occurs, with even more computes taking place.

    How many times will fibonacci get called?

In [1]:
calls = 0
def fibonacci_test(n):
    global calls
    calls += 1
    print('call:', calls, 'n:', n)
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fibonacci_test(n-1) + fibonacci_test(n-2)
In [3]:
fibonacci_test(3)
call: 6 n: 3
call: 7 n: 2
call: 8 n: 1
call: 9 n: 0
call: 10 n: 1
Out[3]:
2
In [4]:
fibonacci(5.1)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[4], line 1
----> 1 fibonacci(5.1)

NameError: name 'fibonacci' is not defined
In [150]:
fibonacci(-1)
---------------------------------------------------------------------------
RecursionError                            Traceback (most recent call last)
Cell In[150], line 1
----> 1 fibonacci(-1)

Cell In[138], line 7, in fibonacci(n)
      5     return 1
      6 else:
----> 7     return fibonacci(n-1) + fibonacci(n-2)

Cell In[138], line 7, in fibonacci(n)
      5     return 1
      6 else:
----> 7     return fibonacci(n-1) + fibonacci(n-2)

    [... skipping similar frames: fibonacci at line 7 (2969 times)]

Cell In[138], line 7, in fibonacci(n)
      5     return 1
      6 else:
----> 7     return fibonacci(n-1) + fibonacci(n-2)

Cell In[138], line 2, in fibonacci(n)
      1 def fibonacci(n):
----> 2     if n == 0:
      3         return 0
      4     elif n == 1:

RecursionError: maximum recursion depth exceeded in comparison

If it worked properly, excellent!

If not, you might want to rewrite your function below to handle such edge cases.

Specifically, have the function return the value -1 for invalid n.

Reminder: the sequence is defined for whole numbers (0, 1, 2, ...)

Call the function again, and verify the cases work properly.

In [5]:
def fibonacci_robust(n):
    
    if (not isinstance(n, int)) or (n < 0):
        return -1
    
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fibonacci_robust(n-1) + fibonacci_robust(n-2)
In [6]:
# edge cases
print(fibonacci_robust(5.1))
print(fibonacci_robust(-1))

# valid cases
print(fibonacci_robust(0))
print(fibonacci_robust(1))
print(fibonacci_robust(2))
print(fibonacci_robust(3))
print(fibonacci_robust(4))
print(fibonacci_robust(5))
-1
-1
0
1
1
2
3
5

Extra¶

Exercise 4.11¶

Demonstration of variable interpolation

Write a Python program that allows users to play the MadLibs® game, using the example story sheet provided.

Capture the values for each blank by using the input() function.

About MadLibs®: In this game, a player is prompted to supply a series of parts of speech (noun, adjective, past tense noun, etc.) while a second player adds these to a story sheet that contains blanks for each response. The story is not visible to the player supplying the words. Once all the blanks are filled, the second player reads the story aloud, which is often funny because of the random way the words are assigned to the story.

This is an example of MadLib story sheet:

Today I went to the zoo. I saw a(n) ___________(adjective)
_____________(Noun) jumping up and down in its tree.
He _____________(verb, past tense) __________(adverb)
through the large tunnel that led to its _______(adjective)
__________(noun). I got some peanuts and passed
them through the cage to a gigantic gray _______(noun)
towering above my head. Feeding that animal made
me hungry. I went to get a __________(adjective) scoop
of ice cream. It filled my stomach. Afterwards I had to
__________(verb) __________ (adverb) to catch our bus.
When I got home I __________(verb, past tense) my
mom for a __________(adjective) day at the zoo.
In [7]:
def madlibs1():
    
    words = dict(adjective1 = '', 
                 noun1 = '', 
                 verb_past_tense1 = '', 
                 adverb1 = '',
                 adjective2 = '', 
                 noun2 = '', 
                 noun3 = '', 
                 adjective3 = '', 
                 verb1 = '', 
                 adverb2 = '', 
                 verb_past_tense2 = '', 
                 adjective4 = '')
    
    for pos in words:
        prompt = pos.replace('_', ' ')[:-1] # Assumes single digit suffix
        words[pos] = input(f"Enter a(n) {prompt}: ")
    
    text = f"Today I went to the zoo.\nI saw a(n) {words['adjective1']} \
{words['noun1']} jumping up and down in its tree.\n\
He {words['verb_past_tense1']} {words['adverb1']} through the large tunnel \
that led to its {words['adjective2']} {words['noun2']}.\nI got some peanuts and passed \
them through the cage to a gigantic gray {words['noun3']} \
towering above my head.\nFeeding that animal made \
me hungry.\nI went to get a {words['adjective3']} scoop \
of ice cream.\nIt filled my stomach.\nAfterwards I had to \
{words['verb1']} {words['adverb2']} to catch our bus.\n\
When I got home I {words['verb_past_tense2']} my \
mom for a {words['adjective4']} day at the zoo."
    
    print(text)

madlibs1()
Today I went to the zoo.
I saw a(n) dfsdf sdf jumping up and down in its tree.
He sdafsd fasd through the large tunnel that led to its fsad fasd.
I got some peanuts and passed them through the cage to a gigantic gray f towering above my head.
Feeding that animal made me hungry.
I went to get a asdf scoop of ice cream.
It filled my stomach.
Afterwards I had to sda f to catch our bus.
When I got home I sadf my mom for a ads day at the zoo.