Problem Set 3: Animating a calendar
Submit this assignment to ps3 on Handin.
Important: Whenever you write a function in this class, follow the design recipe. You will be graded accordingly.
The required number of tests per function is at least the maximum of 2 and the number of cond clauses in the function template.
1 Year, Month, and Day
You will design functions that use dates as conventionally written in English. The following data definitions will be used throughout the problem set, and more will be introduced as they are needed.
; A Year is a non-negative integer ; Examples: ; 0 ; 1789 ; 2018 ; Non-examples: ; -5000 ; "AD 2018" ; A Month is one of: ; - "January" ; - "February" ; - "March" ; - "April" ; - "May" ; - "June" ; - "July" ; - "August" ; - "September" ; - "October" ; - "November" ; - "December" ; A Day is an integer at least 1 but at most 31 ; Examples: ; 1 ; 10 ; 31 ; Non-examples: ; 32 ; "today"
Note that in general, it will be your job to create the data definitions. However, for this problem set we will provide the first steps of the design recipe, including the data definitions, so that you may focus on the subsequent steps in the design recipe.
2 calendar
; calendar : Year Month Day -> Image ; returns an image of a date on a background
; A MonthFormat is one of: ; - "long" ; - "short" ; A DateOrder is one of: ; - "MDY" ; - "DMY"
; format-month : Month MonthFormat -> String ; abbreviates Month to three letters or not (define (format-month m f) ...) (check-expect (format-month "November" "long") "November") (check-expect (format-month "November" "short") "Nov")
Hint: View the input Month as a mere string and use the substring function.
(You may use your solution from Lab 3: Using templates.)
Problem 2. Use format-month to design the following function:
; year-month-day->date : Year Month Day DateOrder MonthFormat -> String ; produces a date as a string ; given: 1936 "November" 12 "MDY" "long" expect: "November 12, 1936" ; given: 1936 "November" 12 "MDY" "short" expect: "Nov 12, 1936" ; given: 1936 "November" 12 "DMY" "long" expect: "12 November 1936" ; given: 1936 "November" 12 "DMY" "short" expect: "12 Nov 1936" (define (year-month-day->date y m d o f) ...)
Use string-append and number->string.
(You may use your solution from Lab 3: Using templates.)
Problem 3. Use year-month-day->date to design the calendar function, the goal of this section. Choose a specific MonthFormat and a specific DateOrder. Also, chose a background image to lay the text on.
; calendar : Year Month Day -> Image ; returns an image of a date on a background
3 days-between
The goal of this section is to design a program that calculates the number of days between two dates. As above, we decompose this task into subtasks to be carried out by helper functions.
To simplify, we pretend that every year has 365 days.
; year-month-day->days : Year Month Day -> Number ; returns the number of days elapsed since January 1, 0 ; given: 0 "January" 1 expect: 0 ; given: 2021 "September" 29 expect: 737936
; month->days-in-year : Month -> Number ; returns the days elapsed in the year before the given month ; given: "January" expect: 0 ; given: "August" expect: 212
Problem 5. Finish designing year-month-day->days. Use month->days-in-year in the definition.
; days-between : Year Month Day Year Month Day -> Number
With year-month-day->days in hand, the definition of days-between is a matter of basic arithmetic. Because days-between should return a non-negative integer, consider using the abs function.
4 semester
In this section, the goal is to design a program which produces a simple animation of calendar. The animation will show the days of this semester passing, using animate.
Take a moment to reflect on this problem and where the difficulty lies. In a sense, we would like to feed calendar to animate, relax and watch the show. However, calendar takes three inputs, not a single number of days.
So we cannot simply compose animate and calendar. But we can design functions which allow us to connect the two. We need to turn a number of days into a Year, into a Month, and into a Day.
; days->year : Number -> Year ; takes days since 1 Jan 0 and returns the year ; given: 364 expect: 0 ; given: 365 expect: 1 ; given: 737936 expect: 2021 ; given: (year-month-day->days 2021 "December" 31) expect: 2021
The last example illustrates that days->year should be sort of an inverse to year-month-day->days. You may find quotient useful here.
Notice that days->year can be used to supply the Year to calendar. We also want days->month and days->day to supply the Month and Day to calendar. But these two functions are more challenging, because month lengths vary. So we provide the following data definitions and template:
; DaysInYear is one of: ; - an integer at least 0 but less than 31 ; - an integer at least 31 but less than 59 ; - an integer at least 59 but less than 90 ; - an integer at least 90 but less than 120 ; - an integer at least 120 but less than 151 ; - an integer at least 151 but less than 181 ; - an integer at least 181 but less than 212 ; - an integer at least 212 but less than 243 ; - an integer at least 243 but less than 273 ; - an integer at least 273 but less than 304 ; - an integer at least 304 but less than 334 ; - an integer at least 334 but less than 365 ; *Interpretation*: the number of elapsed days ; since the first day of the year ; process-days-in-year : DaysInYear -> ... (define (process-days-in-year diy) (cond [(and (>= d 0) (< d 31)) ...] [(and (>= d 31) (< d 59)) ...] [(and (>= d 59) (< d 90)) ...] [(and (>= d 90) (< d 120)) ...] [(and (>= d 120) (< d 151)) ...] [(and (>= d 151) (< d 181)) ...] [(and (>= d 181) (< d 212)) ...] [(and (>= d 212) (< d 243)) ...] [(and (>= d 243) (< d 273)) ...] [(and (>= d 273) (< d 304)) ...] [(and (>= d 304) (< d 334)) ...] [(and (>= d 334) (< d 365)) ...])) ; DaysInMonth is an integer at least 0 but less than 31 ; *Interpretation*: The number of elapsed days ; since the first day of the month
Both days->month and days->day should be defined using helpers.
; days-in-year->month : DaysInYear -> Month ; takes days since the first of the year and returns the month ; given: 0 expect: "January" ; given: 31 expect: "February" ; given: 242 expect: "August" ; days->month : Number -> Month ; takes days since 1 Jan 0 and returns the month ; given: 59 expect: "March" ; given: 364 expect: "December" ; given: 737936 expect: "September" ; given: (year-month-day->days 2021 "December" 31) expect: "December"
; days-in-year->days-in-month : DaysInYear -> DaysInMonth ; takes days since the first of the year ; and returns days since the first of the month ; given: 0 expect: 0 ; given: 59 expect: 0 ; given: 364 expect: 30 ; days->day : Number -> Day ; takes days since 1 Jan 0 and returns the day of the month ; given: 0 expect: 1 ; given: 59 expect: 1 ; given: 737936 expect: 29 ; given: (year-month-day->days 2021 "December" 31) expect: 31
Finally, we are ready to build our animation.
Problem 10. Let’s create an animation which shows the days of this semester passing. The first day of this semester was September 6, 2021. The last day will be December 18, 2021.
Finish designing semester-cal.
; first-day : Number ; The number of days elapsed on September 6, 2021 since January 1, 0 (define first-day ...) ; length-of-semester : Number ; The number of days elapsed on December 18, 2021 since September 6, 2021 (define length-of-semester ...) ; a SemesterDay is one of: ; - an integer n, 0 <= n < length-of-semester ; *Interpretation*: n days have elapsed since September 6, 2021 ; - an integer n, n >= length-of-semester ; *Interpretation*: The semester has ended ; semester-cal : SemesterDay -> Image ; takes a number of days since the first day of the semester ; and returns a calendar image of the corresponding date; does ; not advance past the last day of the semester (define (semester-cal t) ...)
If you pass semester-cal to animate, you should see an animation that starts on September 6, 2021, passes through the days quickly, and stops on December 18, 2021.
The calendar pages in this animation pass very quickly: 28 per second to be exact. Let’s slow it down. Pick how many calendar pages per second you would like to see and define this number as cal-rate. You can then convert an animation tick number to a SemesterDay, by multiplying by cal-rate and then quotienting by 28.
Design a function semester which takes a tick number, converts it to a SemesterDay and passes the result to semester-cal to produce an image.
Your final program should produce a running animation using semester.