A document from MCS 275 Spring 2022, instructor David Dumas. You can also get the notebook file.

MCS 275 Spring 2022 Homework 5 Solutions

  • Course Instructor: David Dumas
  • Solutions prepared by: Johnny Joyce

Instructions:

  • Complete the problems below, which ask you to write Python scripts.
  • Upload your python code directly to gradescope, i.e. upload the .py files containing your work. (If you upload a screenshot or other file format, you won't get credit.)

Deadline

This homework assignment must be submitted in Gradescope by Noon central time on Tuesday 15 February 2022.

Collaboration

Collaboration is prohibited, and you may only access resources (books, online, etc.) listed below.

Resources you may consult

The course materials you may refer to for this homework are:

Point distribution

This homework assignment has two problems, numbered 2 and 3. The grading breakdown is:

Points Item
2 Autograder
4 Problem 2
4 Problem 3
10 Total

The part marked "autograder" reflects points assigned to your submission based on some simple automated checks for Python syntax, etc.. The result of these checks is shown immediately after you submit.

What to do if you're stuck

Ask your instructor or TA a question by email, in office hours, or on discord.

Problem 1 doesn't exist

In Gradescope, the score assigned to your homework submission by the autograder (checking for syntax and docstrings) will be recorded as "Problem 1". Therefore, the numbering of the actual problems begins with 2.

Problem 2: WorkingHours context manager

In Python, you can check the current day and time by importing the datetime module and then calling datetime.datetime.now(). The return value is an object with attributes and methods to tell you things like the year, month, day, hour, minute, second, and day of week. For example, this script will print the current hour and weekday:

In [189]:
import datetime

dt = datetime.datetime.now()

print("Right now, the hour (in 24-hour time) is: ",dt.hour)
day_number = dt.weekday() # 0 for Monday, 1 for Tuesday, etc.

day_names = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
print("And the day of week is {}, which is day number {} in Python's numbering system.".format(
    day_names[day_number],
    day_number
))
Right now, the hour (in 24-hour time) is:  15
And the day of week is Thursday, which is day number 3 in Python's numbering system.

Write a context manager WorkingHours() that makes sure a block of code starts running between 9am and 5pm on a Monday, Tuesday, Wednesday, Thursday, or Friday. For the purposes of this question, we'll call those "working hours". So the desired behavior of this context manager is:

  • If __enter__ is called during working hours, it should return immediately
  • If __enter__ is called at any other time, it should pause for 60 seconds, check again, etc., not returning until it is during working hours.

This context manager doesn't need to do any cleanup at the end of the with block, but it still needs to have a method called __exit__ in order for Python to recognize it as a context manager.

Put your context manager class in a file called hwk5prob2.py and upload it to gradescope.

Solution

In [1]:
import datetime
import time

class WorkingHours():
    '''Context manager to wait until working hours (9AM-5PM on Mon/Tue/Wed/Thu/Fri)'''
    
    def __enter__(self):
        '''Upon entering context manager, keep trying to check if time is in working hours'''
        while True: # Use an infinite loop to keep checking time
            
            dt = datetime.datetime.now()
            if dt.weekday() in [0,1,2,3,4] and 9 <= dt.hour < 17: # 0=Mon, 1=Tue, 2=Wed, etc.
                return
            else:
                # print("Sleeping for 60 seconds. Zzz...")  # optional, for verbose output
                time.sleep(60)
    
        
    def __exit__(self, *args, **kwargs): # Extra args are given to represent if any errors occur
        '''No cleanup necessary'''
        pass # or we could omit this line entirely, as a docstring alone is OK
    
with WorkingHours():
    print("Hello! We are currently in working hours.")
Hello! We are currently in working hours.

Problem 3: Tangled web of function calls

Consider the three functions below. They call one another in a rather complicated way!

In [186]:
def alpha(x):
    if x<0:
        return 0                         # a
    elif x%7 < 3:
        return beta(x-1)                 # b
    else:
        return gamma(x-1) + beta(x//3)   # c
    
def beta(x):
    if x%9 < 5:
        return alpha(x-2)                # d
    else:
        return gamma(x-1) + alpha(2*x-4) # e
    
def gamma(x):
    if x%275 > 10:
        return x**abs(x) % 13            # f
    else:
        return alpha((9*x)//10)          # g

Use any debugging techniques you like (pdb, print, etc.) to answer the following questions. Type your answers in a file called hwk5prob3.txt that you create using Visual Studio Code, and then upload that file to gradescope. Note that you're not uploading any Python code for this question, you're just typing answers into a file and uploading that file.

Solutions


3A.

Calling alpha(24) returns 14, as you can check. In doing so, how many many times is each function alpha, beta, and gamma called? (Give a separate total for each one.)

Solution:

alpha is called 11 times

beta is called 7 times

gamma is called 6 times


3B.

In computing alpha(24), what is the largest value of x that is passed to any of the functions?

Solution:

If we count the initial call to alpha, then the largest value is 24. Otherwise, it is 23, which is passed to gamma.


3C.

Some of the lines of code contain comments labeling them with the letters a-g. In computing alpha(24), does each labeled line of code run at least once? If not, which of them never run?

Solution:

All of the labeled lines run at least once.


3D.

Are there any other values of x between 0 and 100 so that alpha(x) returns 14? (Some values of x will cause a RecursionError exception to be raised, so you'll need to account for that in automating a search.)

Solution:

In [3]:
import sys
sys.setrecursionlimit(1000)
# Set the maximum recursion depth to 1000 so it doesn't take too long.
# For many (but not all) setups, 1000 is the default anyway


for x in range(100):
    try:
        if alpha(x) == 14:
            print("alpha({}) returns 14".format(x))
    except RecursionError: # If we get a RecursionError, just ignore it and go to the next value
        continue
alpha(15) returns 14
alpha(24) returns 14