Exercises#
Check your understanding. These exercises are taken from old exams, which implies that you should be able to complete them pretty quickly.
Finish function
count_ge
def count_ge(nums: list[int], at_least: int) -> int:
"""Returns a count of elements in nums whose value is
at least (>=) at_least.
"""
return 0 # FIXME
print(count_ge([1, 2, 3, 4, 5], 3)) # Expecting 3
print(count_ge([-10, 17, -3, 5], 0)) # Expecting 2
print(count_ge([], 0)) # Expecting 0
print(count_ge([7, 12, 17], 99)) # Expecting 0
Complete function
longest_word. Recall thatlen(s)returns the length of strings.
def longest_word(words: list[str]) -> str:
"""Return the longest element of words, which must be a
non-empty list of strings. In case of a tie, return the
first of the longest elements.
"""
return "NOT IMPLEMENTED YET" # FIXME
print(longest_word(['we', 'know', 'how', 'to', 'select', 'extremes']))
# Expect 'extremes'
print(longest_word(['we', 'already', 'know', 'how']))
# Expect 'already'
print(longest_word(['really?']))
# Expect 'really?'
We classify some letters as vowels, others as consonants. Some characters (such as “-”) are neither vowels nor consonants. In this problem, you are given a function
is_vowel, which you must use.
You will write function vowel_ratio, which returns the ratio of vowels to string length as a floating point number. For example, “bash” has one vowel and three characters that are not vowels, so its vowel_ratio is 1/4 or 0.25.
All strings passed to vowel_ratio will have at least one character.
def is_vowel(s: str) -> bool:
"""Returns true if and only if s is a vowel"""
return (s.lower() in "aeiou")
def vowel_ratio(s: str) -> float:
"""Returns the fraction between 0.0 and 1.0 of characters
ch in s, a non-empty string, for which is_vowel(ch) is true.
"""
return 0 # FIXME
print(vowel_ratio("bear")) # Expect 0.5
print(vowel_ratio("honey")) # Expect 0.4
print(vowel_ratio("xXx")) # Expect 0.0
print(vowel_ratio("aAa")) # Expect 1.0
Complete function
count_one_value.
def count_one_value(k: str, li: list[str]) -> int:
"""How many occurrences of k are in li?"""
return 0 # FIXME
print(count_one_value("duck", ["duck", "goose", "duck", "duck"]))
# Expect 3
print(count_one_value("t-rex", ["duck", "goose", "duck", "duck"]))
# Expect 0
Complete function
count_all_values.
def count_all_values(li: list[str]) -> dict[str, int]:
"""Returns dict with counts of strings in li.
"""
return {} # FIXME
print(count_all_values(["carrot", "chocolate", "carrot", "strawberry"]))
# Expect {'carrot': 2, 'chocolate': 1, 'strawberry': 1}
print(count_all_values([]))
# Expect {}
Complete function
select_by_value.
def select_by_value(min_val: int, li: list[str], values: dict[str, int]) -> list[str]:
"""Returns list of elements of li associated with a value at least min_val in values.
(Assume all elements in li are keys in values)
"""
return [] # FIXME
print(select_by_value(5, ["dog", "cat", "rabbit"], {"cat": 7, "rabbit": 3, "dog": 9}))
# Expect ['dog', 'cat']
print(select_by_value(17, ["dog", "cat", "rabbit"], {"cat": 12, "rabbit": 4, "dog": 9}))
# Expect []
Answers to Exercises#
Here is one possible way to finish
count_ge
def count_ge(nums: list[int], at_least: int) -> int:
"""Returns a count of elements in nums whose value is
at least (>=) at_least.
"""
count = 0
for n in nums:
if n >= at_least:
count += 1
return count
print(count_ge([1, 2, 3, 4, 5], 3)) # Expecting 3
print(count_ge([-10, 17, -3, 5], 0)) # Expecting 2
print(count_ge([], 0)) # Expecting 0
print(count_ge([7, 12, 17], 99)) # Expecting 0
3
2
0
0
Here is a possible completion of
longest_word.
def longest_word(words: list[str]) -> str:
"""Return the longest element of words, which must be a
non-empty list of strings. In case of a tie, return the
first of the longest elements.
"""
longest = words[0]
for word in words:
if len(word) > len(longest):
longest = word
return longest
print(longest_word(['we', 'know', 'how', 'to', 'select', 'extremes']))
# Expect 'extremes'
print(longest_word(['we', 'already', 'know', 'how']))
# Expect 'already'
print(longest_word(['really?']))
# Expect 'really?'
extremes
already
really?
Here is a function vowel_ratio, which returns the ratio of vowels to string length as a floating point number, using function
is_vowel.
def is_vowel(s: str) -> bool:
"""Returns true if and only if s is a vowel"""
return (s.lower() in "aeiou")
def vowel_ratio(s: str) -> float:
"""Returns the fraction between 0.0 and 1.0 of characters
ch in s, a non-empty string, for which is_vowel(ch) is true.
"""
vowel_count = 0
total_count = 0
for ch in s:
total_count += 1
if is_vowel(ch):
vowel_count += 1
return vowel_count / total_count
print(vowel_ratio("bear")) # Expect 0.5
print(vowel_ratio("honey")) # Expect 0.4
print(vowel_ratio("xXx")) # Expect 0.0
print(vowel_ratio("aAa")) # Expect 1.0
0.5
0.4
0.0
1.0
Function
count_one_valueis an example of the accumulator pattern.
def count_one_value(k: str, li: list[str]) -> int:
"""How many occurrences of k are in li?"""
count = 0
for el in li:
if el == k:
count += 1
return count
print(count_one_value("duck", ["duck", "goose", "duck", "duck"]))
# Expect 3
print(count_one_value("t-rex", ["duck", "goose", "duck", "duck"]))
# Expect 0
3
0
count_all_valuesis a variation on our project for analyzing class enrollments (just the counting part). We can consider it a variation on the accumulator pattern, but we are accumiulating a count for each distinct string, using adict.
def count_all_values(li: list[str]) -> dict[str, int]:
"""Returns dict with counts of strings in li.
"""
counts = {}
for el in li:
if el in counts:
counts[el] += 1
else:
counts[el] = 1
return counts
print(count_all_values(["carrot", "chocolate", "carrot", "strawberry"]))
# Expect {'carrot': 2, 'chocolate': 1, 'strawberry': 1}
print(count_all_values([]))
# Expect {}
{'carrot': 2, 'chocolate': 1, 'strawberry': 1}
{}
The pattern of
select_by_valueis sometimes called a filter.
Be sure to solve it by building a new list with just the selected items, and not by removing elements fromli. You need to put together a couple patterns you know (filtering a collection, using adict) to solve a new problem. Computational problem solving often involves combining and customizing familiar patterns into novel forms.
def select_by_value(min_val: int, li: list[str], values: dict[str, int]) -> list[str]:
"""Returns list of elements of li associated with a value at least min_val in values.
(Assume all elements in li are keys in values)
"""
result = []
for el in li:
if values[el] >= min_val:
result.append(el)
return result
print(select_by_value(5, ["dog", "cat", "rabbit"], {"cat": 7, "rabbit": 3, "dog": 9}))
# Expect ['dog', 'cat']
print(select_by_value(17, ["dog", "cat", "rabbit"], {"cat": 12, "rabbit": 4, "dog": 9}))
# Expect []
['dog', 'cat']
[]
A common mistake is making a sequential search of values for each
element of li. We use the
dict data structure to avoid linear searches when we can. If we
had 1000 elements in li and 1000 (key, value) pairs in
values, the nested loops for sequentially searching
values would require 1,000,000 comparisons, while looking up
values with dictionary access if values[item] >= min_value:
would require only 1000 dictionary lookups. Any time you see a
dict, you should expect that it is there so that we can access
items directly by key rather than making a linear search of the
structure.