HW1

Instructions

Get the starter code.

Due: Wednesday, October 11, 11:59pm Pacific Time

You will write 12 OCaml functions (and tests for them) related to calendar dates.

In all problems, a "date" is an OCaml value of type int * int * int, where the first part is the day-of-the-month, the second part is the month, and the third part is the year. For example, (2, 10, 2023) represents the second day of the tenth month (October) in the year 2023.

A "reasonable" date has a positive year, a month between 1 and 12 (inclusive on both ends), and a day no greater than 31. For full credit, you only need to handle reasonable dates, but you should not explicitly check whether your input is reasonable, and your solution will naturally work correctly for some non-reasonable dates, too.

A "day of year" is a number between 1 and 365 (inclusive on both ends). For example, 33 represents February 2nd. We ignore leap years.

To access elements of a date, use the functions fst3, snd3, and thd3 provided in the starter code to access the first, second, and third components, respectively. You do not need to understand how these functions are implemented; we will study that next week.

When writing your solution, feel free to refer to the library functions listed at the end of this document.

When we ask you to write tests, you should cover all branches in the current function. For recursive functions, you should cover the case where 0, 1, and more than 1 recursive calls are made. You should also cover any boundary cases as you see fit. You should generally not test the behavior of your functions on inputs that violate assumptions that the problems tell you to make.

The problems

  1. Write a function is_older that takes two dates and evaluates to true or false. It evaluates to true if the first argument is a date that comes strictly earlier in time than the second argument. (If the two dates are the same, the result is false.)

    Write tests to convince your reader that your function is correct.

  2. Write a function number_in_month that takes a list of dates and a month (i.e., an int) and returns how many dates in the list are in the given month.

    Write tests to convince your reader that your function is correct.

  3. Write a function number_in_months that takes a list of dates and a list of months (i.e., an int list), and returns the number of dates in the list of dates that are in any of the months in the list of months. Assume the list of months has no duplicates.

    Write tests to convince your reader that your function is correct.

    Hint: Use your answer to the previous problem.

  4. Write a function dates_in_month that takes a list of dates and a month (i.e., an int) and returns a list holding the dates from the argument list of dates that are in the month. The returned list should contain the dates in the order they were originally given.

    Write tests to convince your reader that your function is correct.

  5. Write a function dates_in_months that takes a list of dates and a list of months (i.e., an int list) and returns a list holding the dates from the argument list of dates that are in any of the months in the list of months. Assume the list of months has no duplicates.

    Write tests to convince your reader that your function is correct.

    Hint: Use your answer to the previous problem and OCaml's list-append operator.

  6. Write a function get_nth that takes a list of strings and a positive int \(n\) and returns the \(n^\mathrm{th}\) element of the list where the head of the list is \(n = 1\). (In other words, use 1-indexing, not 0-indexing!) Do not worry about the case where the list has too few elements—your function may apply List.hd or List.tl to the empty list in this case, which is okay, and you should not check for this case explicitly.

    Write tests to convince your reader that your function is correct.

  7. Write a function string_of_date that takes a date and returns a string of the form October-4-2023 (for example). Use the operator ^ for concatenating strings and the library function string_of_int for converting an int to a string. For producing the month part, do not use a bunch of conditionals. Instead, use a list holding 12 strings and access it using your answer to the previous problem. For consistency, use hyphens exactly as in the example and use capitalized English month names: January, February, March, April, May, June, July, August, September, October, November, December.

    Write tests to convince your reader that your function is correct.

  8. Write a function number_before_reaching_sum that takes an int called \(\mathit{sum}\), which you can assume is positive, and an int list, which you can assume contains only positive numbers, and returns an int. You should return an int \(n\) such that the first \(n\) elements of the list add to less than \(\mathit{sum}\), but the first \(n+1\) elements of the list add to \(\mathit{sum}\) or more. Assume the entire list adds to more than \(\mathit{sum}\)—it is okay for an exception to occur if this assumption is violated, and you should not check for this case explicitly.

    Write tests to convince your reader that your function is correct.

  9. Write a function what_month that takes a day of the year (i.e., an int between 1 and 365 inclusive on both ends) and returns an int indicating what month that day is in. Use a list holding 12 integers and your answer to the previous problem. For example, if the input is 35, the output should be 2, because 35 represents February 4th, and February is the 2nd month of the year.

    Write tests to convince your reader that your function is correct.

  10. Write a function month_range that takes two days of the year, day1 and day2, and returns an int list [m1; m2; ...; mn] where m1 is the month of day1, m2 is the month of day1 + 1, ..., and mn is the month of day2. Note the result will have length day2 - day1 + 1 or length 0 if day1 > day2.

    Write tests to convince your reader that your function is correct.

  11. Write a function oldest that takes a list of dates and returns an (int*int*int) option. It evaluates to None if the list is empty; otherwise, it evaluates to Some d where d is the oldest date in the list. (Remember that "oldest" means "earliest in time".)

    Write tests to convince your reader that your function is correct.

    Hint: Use is_older to compare dates.

  12. Write a function cumulative_sum that takes a list of numbers and returns a list of the partial sums of these numbers. For example, cumulative_sum [12; 27; 13] = [12; 39; 52].

    Write tests to convince your reader that your function is correct.

    Hint: Use a helper function that takes two arguments.

Notes

  • There may be some problems that ask you to do something similar to a function in the standard library. Do not copy the code from the standard library, because it uses features we have not covered in class.

  • The sample solution is approximately 100 lines of code.

Summary

Evaluating your homework solution should generate these bindings:

val is_older : (int * int * int) * (int * int * int) -> bool
val number_in_month : (int * int * int) list * int -> int
val number_in_months : (int * int * int) list * int list -> int
val dates_in_month : (int * int * int) list * int -> (int * int * int) list
val dates_in_months : (int * int * int) list * int list -> (int * int * int) list
val get_nth : string list * int -> string
val string_of_date : int * int * int -> string
val number_before_reaching_sum : int * int list -> int
val what_month : int -> int
val month_range : int * int -> int list
val oldest : (int * int * int) list -> (int * int * int) option
val cumulative_sum : int list -> int list

Generating these bindings does not guarantee that your solutions are correct.

Include your tests in the same file as your solutions. Your tests will generate additional bindings.

Upload your code as a file called hw1.ml to Gradescope.