A document from MCS 260 Fall 2021, instructor David Dumas. You can also get the notebook file.

MCS 260 Fall 2021 Worksheet 5 Solutions

  • Course instructor: David Dumas

Topics

We covered a lot of new ground last week. This worksheet focuses on:

  • functions
  • dictionaries
  • additional features of strings (e.g. .split(), .strip(), .replace())
  • the math module
  • the random module

Instructions

  • Work on the exercises below during lab. Complete them afterward if you don't finish during the lab period.
  • This worksheet will prepare you for the upcoming homework assignment.
  • Most of the exercises ask you to write Python scripts to accomplish a certain task. We recommend making a folder (with a name like "worksheet5") to store the scripts you prepare today.
  • Seek assistance from your TA or from fellow students if you run into trouble.

Collaboration

Collaboration on worksheets is strongly encouraged.

Resources

The main course materials to refer to for this worksheet are:

(Lecture videos are not linked on worksheets, but are also useful to review while working on worksheets. Video links can be found in the course course Blackboard site.)

1. Divisors

Let n and k be positive integers. We say that k is a divisor of n if n == k*j where j is also an integer. For example:

  • 1 is a divisor of 20 because 20 = 1*20
  • 2 is a divisor of 20 because 20 = 2*10
  • 3 is not a divisor of 20
  • 4 is a divisor of 20 because 20 = 4*5
  • 5 is a divisor of 20 because 20 = 5*4
  • 6 is not a divisor of 20

Equivalently, k is a divisor of n if dividing n by k gives zero remainder.

Write a function divisors(n) that takes an integer n and returns a list of all the divisors of n.

Your function should look like this:

In [ ]:
def divisors(n):
    # code here
    return   # <--- return value (a list) needs to go here

When you create such a function, it won't do anything unless you call it, so you should write some code to test the function. Here is some code that you can paste below your function to make a program that will test the function's behavior:

In [ ]:
#-- begin test code you should paste below the function you write --
print("Input 20")
print("Expected return value is: [1, 2, 4, 5, 10, 20]")
print("  Actual return value is:",divisors(20))
print()
print("Input 7")
print("Expected return value is: [1, 7]")
print("  Actual return value is:",divisors(7))
print()
print("Input 115")
print("Expected return value is: [1, 5, 23, 115]")
print("  Actual return value is:",divisors(115))
#-- end test code you should paste below the function you write --

If everything is working properly, your program should then print:

Input 20
Expected return value is: [1, 2, 4, 5, 10, 20]
  Actual return value is: [1, 2, 4, 5, 10, 20]

Input 7
Expected return value is: [1, 7]
  Actual return value is: [1, 7]

Input 115
Expected return value is: [1, 5, 23, 115]
  Actual return value is: [1, 5, 23, 115]

Solution

In [3]:
def divisors(n):
    """Return a list of divisors of an integer `n`"""
    return [ k for k in range(1,n+1) if n%k==0 ]

2. Startup idea generator

A common type of idea for a new business is to make a variation on an existing product that has a unique characteristic or new twist, e.g. "a juice machine that is also an exercise bike".

Here is a list of some products:

In [7]:
product_ideas = [
    "a juice machine",
    "a carpet",
    "an office chair",
    "a coffee maker",
    "a haircut robot",
    "a Python course",
    "a toothbrush",
    "a pair of noise-cancelling headphones",
    "an oversized raccoon plush doll",
    "a detailed model of Boise, Idaho"
]

and here is a list of some special characteristics:

In [8]:
special_features = [
    "that is controlled by a smartphone app",
    "that plays polka music",
    "made of polished titanium",
    "that also mines bitcoin",
    "scented with sandalwood and lime oil",
    "with a pleasant strawberry flavor",
    "run by an elite squad of monks trained in martial arts", 
    "without spiders",
    "with an integrated soap dispenser",
    "that is vegetarian and gluten-free",
    "enriched with vitamin D",
    "that fires plastic darts"
]

Write a function startup_idea() that chooses a random product idea and special feature, and combines them into a string like

a carpet that fires plastic darts

which is returned. The function shouldn't display anything; it returns the idea as a string.

Then, use this function in a program startupidea.py that repeatedly suggests a random startup idea, asking the user if they are happy with it. If they say yes, it exits. Otherwise, it keeps suggesting.

Here is what it should look like when the program is run:

Startup idea: a carpet that also mines bitcoin
Acceptable?  (Y/N): n
Startup idea: an oversized raccoon plush doll without spiders
Acceptable?  (Y/N): n
Startup idea: a pair of noise-cancelling headphones enriched with vitamin D
Acceptable?  (Y/N): n
Startup idea: a toothbrush enriched with vitamin D
Acceptable?  (Y/N): n
Startup idea: a juice machine scented with sandalwood and lime oil
Acceptable?  (Y/N): y

(You are welcome to add some of your own product or special feature ideas to the lists.)

Solution

In [14]:
import random 

product_ideas = [
    "a juice machine",
    "a carpet",
    "an office chair",
    "a coffee maker",
    "a haircut robot",
    "a Python course",
    "a toothbrush",
    "a pair of noise-cancelling headphones",
    "an oversized raccoon plush doll",
    "a detailed model of Boise, Idaho",
    "a university building"
]
special_features = [
    "that is controlled by a smartphone app",
    "that plays polka music",
    "made of polished titanium",
    "that also mines bitcoin",
    "scented with sandalwood and lime oil",
    "with a pleasant strawberry flavor",
    "run by an elite squad of monks trained in martial arts", 
    "without spiders",
    "with an integrated soap dispenser",
    "that is vegetarian and gluten-free",
    "enriched with vitamin D",
    "that fires plastic darts",
    "made of hexagons and confusion"
]

def startup_idea():
    """Produce a random startup idea from a product and special feature"""
    return random.choice(product_ideas) + " " + random.choice(special_features)

acceptable = "n"
while acceptable != "y":
    print("Startup idea:",startup_idea())
    acceptable = input("Acceptable? (Y/N): ").lower()
Startup idea: a coffee maker that fires plastic darts
Acceptable? (Y/N): n
Startup idea: an office chair that is vegetarian and gluten-free
Acceptable? (Y/N): n
Startup idea: a university building with a pleasant strawberry flavor
Acceptable? (Y/N): n
Startup idea: a university building that is controlled by a smartphone app
Acceptable? (Y/N): n
Startup idea: an office chair that fires plastic darts
Acceptable? (Y/N): n
Startup idea: a coffee maker that fires plastic darts
Acceptable? (Y/N): n
Startup idea: a haircut robot that fires plastic darts
Acceptable? (Y/N): n
Startup idea: a detailed model of Boise, Idaho that is vegetarian and gluten-free
Acceptable? (Y/N): n
Startup idea: a juice machine without spiders
Acceptable? (Y/N): y

3. Dictionary user interface

Write a program that sits in an infinite loop waiting for user commands. The command can be either one word or two words (separated by spaces).

A two-word command is a request for the program to remember a fact, e.g. the command

name David

is a request for the program to remember that the key "name" is associated with the value "David", and

occupation horticulturist

is a request for the program to remember that the key "occupation" is associated with the value "horiculturist".

A one-word command is a request to look up the value associated to a key. E.g. the command

name

asks the program to print the value associated with the key "name". If there is no value associated with a given key, the program should print "KEY NOT FOUND".

The program should exit if a blank command is entered.

Here is a sample session using the program:

> name David
> name
David
> occupation mathematician
> color orange
> beverage coffee
> color
orange
> height
KEY NOT FOUND
> beverage
coffee
> beverage water
> beverage
water
> name
David
> name Parthanax
> occupation
mathematician
> name
Parthanax
>

Hint: You should use a dictionary in your program. You probably want to start with an empty dictionary. The function dict() will create and return an empty dictionary, or you can use the literal {}.

Hint: The trickiest part of this program is probably the logic needed to split the the input string into words but then do something different depending on whether that list has length 1 or 2.

Solution

In [6]:
data = dict()

while True:
    s = input("> ")
    if not s: # i.e. if s is empty
        break
    
    words = s.split()
    if len(words)==1:
        # Request for value of key words[0]
        if words[0] in data:
            print(data[words[0]])
        else:
            print("KEY NOT FOUND")
    else:
        # Request to set key words[0] to value words[1]
        data[words[0]] = words[1]
> name David
> name
David
> occupation mathematician
> color orange
> beverage coffee
> color
orange
> height
KEY NOT FOUND
> beverage
coffee
> beverage water
> beverage
water
> name
David
> name Parthanax
> occupation
mathematician
> name
Parthanax
> 

4. Improved word stats

Modify the example program wordstats.py so that it converts all of the following punctuation characters from the string text into spaces before doing the word count:

  • period .
  • question mark ?
  • comma ,
  • single quotation mark / apostrophe '
  • double quotation mark "
  • colon :
  • semicolon ;
  • dash -
  • start parenthesis (
  • end parenthesis )

Rather than chaining many method calls together, use a loop to do this. That is, make a list like

In [24]:
punctuation = [".","?",",","\'","\"",":",";","-","(",")"]

and then use a for loop to consider each punctuation character in turn, replacing any instances of it in text with spaces.

Then, test it with the text shown below instead of the CFR passage we used in lecture. (This is taken from The Adventures of Sherlock Holmes by Arthur Conan Doyle.)

The King took a heavy chamois leather bag from under his cloak and laid
it on the table.

"There are three hundred pounds in gold and seven hundred in notes," he
said.

Holmes scribbled a receipt upon a sheet of his notebook and handed it
to him.

"And Mademoiselle's address?" he asked.

"Is Briony Lodge, Serpentine Avenue, St. John's Wood."

Holmes took a note of it. "One other question," said he. "Was the
photograph a cabinet?"

"It was."

"Then, good-night, your Majesty, and I trust that we shall soon have
some good news for you. And good night, Watson," he added, as the
wheels of the royal brougham rolled down the street. "If you will be
good enough to call tomorrow afternoon at three o'clock I should like
to chat this little matter over with you."

Save the new program as wordstats2.py.

Solution

Note: The method proposed in the problem is not perfect: It considers an apostrophe to be punctuation, because the same character can be used as a single quotation mark. As a result, possessives like "Mademoiselle's" are considered to be two words. However, we do as the problem asks.

In [15]:
# Contents of wordstats2.py

text = """The King took a heavy chamois leather bag from under his cloak and laid
it on the table.

"There are three hundred pounds in gold and seven hundred in notes," he
said.

Holmes scribbled a receipt upon a sheet of his notebook and handed it
to him.

"And Mademoiselle's address?" he asked.

"Is Briony Lodge, Serpentine Avenue, St. John's Wood."

Holmes took a note of it. "One other question," said he. "Was the
photograph a cabinet?"

"It was."

"Then, good-night, your Majesty, and I trust that we shall soon have
some good news for you. And good night, Watson," he added, as the
wheels of the royal brougham rolled down the street. "If you will be
good enough to call tomorrow afternoon at three o'clock I should like
to chat this little matter over with you.\""""

wordcount = 0
words_seen = []

punctuation = [".","?",",","\'","\"",":",";","-","(",")"]

filtered_text = text.lower() # convert to lower case
# Replace punctuation with spaces
for c in punctuation:
    filtered_text = filtered_text.replace(c," ")

for w in filtered_text.split():
    wordcount = wordcount + 1
    if w not in words_seen:
        words_seen.append(w)
        

print("Analyzing this text:")
print(text)
print()
print("Words:",wordcount)
print("Distinct words:",len(words_seen))
print("List of distinct words:",words_seen)
Analyzing this text:
The King took a heavy chamois leather bag from under his cloak and laid
it on the table.

"There are three hundred pounds in gold and seven hundred in notes," he
said.

Holmes scribbled a receipt upon a sheet of his notebook and handed it
to him.

"And Mademoiselle's address?" he asked.

"Is Briony Lodge, Serpentine Avenue, St. John's Wood."

Holmes took a note of it. "One other question," said he. "Was the
photograph a cabinet?"

"It was."

"Then, good-night, your Majesty, and I trust that we shall soon have
some good news for you. And good night, Watson," he added, as the
wheels of the royal brougham rolled down the street. "If you will be
good enough to call tomorrow afternoon at three o'clock I should like
to chat this little matter over with you."

Words: 140
Distinct words: 100
List of distinct words: ['the', 'king', 'took', 'a', 'heavy', 'chamois', 'leather', 'bag', 'from', 'under', 'his', 'cloak', 'and', 'laid', 'it', 'on', 'table', 'there', 'are', 'three', 'hundred', 'pounds', 'in', 'gold', 'seven', 'notes', 'he', 'said', 'holmes', 'scribbled', 'receipt', 'upon', 'sheet', 'of', 'notebook', 'handed', 'to', 'him', 'mademoiselle', 's', 'address', 'asked', 'is', 'briony', 'lodge', 'serpentine', 'avenue', 'st', 'john', 'wood', 'note', 'one', 'other', 'question', 'was', 'photograph', 'cabinet', 'then', 'good', 'night', 'your', 'majesty', 'i', 'trust', 'that', 'we', 'shall', 'soon', 'have', 'some', 'news', 'for', 'you', 'watson', 'added', 'as', 'wheels', 'royal', 'brougham', 'rolled', 'down', 'street', 'if', 'will', 'be', 'enough', 'call', 'tomorrow', 'afternoon', 'at', 'o', 'clock', 'should', 'like', 'chat', 'this', 'little', 'matter', 'over', 'with']

Revision history

  • 2021-09-23 Initial release
  • 2021-09-24 Fix wordstats2.py so it prints the original text, not the filtered version with punctuation removed.