2
\$\begingroup\$

I'm an experienced programmer trying to learn OCaml. I figured a good way to do that would be to pick a simple task on Rosetta code that didn't yet have an OCaml example and do it myself. With that in mind, I wrote this program to find the last sunday of each month in a given year. It works by using Gauss's algorithm to determine what day of the week the last day in each month of the year is and if it's not a Sunday it moves backwards until it is. I run it with "./ocaml sundays.ml N" where N is the year in question. Are there any spots where my code is written in a way unusual for OCaml developers or where there is a more OCamlish way of doing things?

let is_leap_year y =
  (* See OCaml solution on Rosetta Code for 
     determing if it's a leap year *) 
   if (y mod 100) = 0
     then (y mod 400) = 0
     else (y mod 4) = 0;;

let get_days aYear =
  if is_leap_year aYear
    then
      [31;29;31;30;31;30;31;31;30;31;30;31]
    else
      [31;28;31;30;31;30;31;31;30;31;30;31];;

let print_date y m d =
  print_int y;
  print_char '/';
  print_int m;
  print_char '/';
  print_int d;
  print_endline "";;

let get_day_of_week y m d =
  let y = if m > 2 then y else y - 1 in
  let c = y / 100 in
  let y = y mod 100 in
  let m_shifted = float_of_int ( ((m + 9) mod 12) + 1) in
  let m_factor = int_of_float (2.6 *. m_shifted -. 0.2) in
  let leap_factor = 5 * (y mod 4) + 3 * (y mod 7) + 5 * (c mod 4) in
  (d + m_factor + leap_factor) mod 7;; 

let get_shift y m last_day =
  get_day_of_week y m last_day;;

let print_last_sunday y m =
  let days = get_days y in
  let last_day = List.nth days (m - 1) in
  let last_sunday = last_day - (get_shift  y m last_day) in
  print_date y m last_sunday;;

let print_last_sundays y =
  let months = [1;2;3;4;5;6;7;8;9;10;11;12] in
  List.iter (print_last_sunday y) months;;

match (Array.length Sys.argv ) with 
  2 -> print_last_sundays( int_of_string (Sys.argv.(1)));
 |_ -> invalid_arg "Please enter a year";
\$\endgroup\$
4
\$\begingroup\$
  1. You do not need to put ;; everywhere. They're artifact of the interpreter.

  2. print_date can be rewritten using printf:

    let print_date = Printf.printf "%d/%d/%d"
    
  3. It is better to store days in an array. List is not a good container for random access. Also, syntactically it would be easier to read: days.(2) is better than List.nth 2. Also, just FYI, you can have arrays in OCaml indexed from one, e.g., let days = Bigarray.(Array1.create int fortran_layout 12). Although, using them here would be overkill, I think.

  4. You do not need parens here:

    let last_sunday = last_day - (get_shift  y m last_day) in
    
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.