Problem19
30分プログラム、その285。Problem 19 via Project Euler。
次の情報が与えられている。
- 1900年1月1日は月曜日である。
- 9月、4月、6月、11月は30日まであり、2月を除く他の月は31日まである。
- 2月は28日まであるが、うるう年のときは29日である。
- うるう年は西暦が4で割り切れる年に起こる。しかし、西暦が400で割り切れず100で割り切れる年はうるう年でない。
20世紀(1901年1月1日から2000年12月31日)で月の初めの日曜日の数を数えよ。
特に工夫をせず、1900/1/1からの経過日を持ち回って、曜日を計算してみた。
使い方
$ ocamlbuild problem19.native Finished, 4 targets (2 cached) in 00:00:01. $ ./problem19.native 171
OCamlBuildを始めて使ってみた。これは便利だ。
ソースコード
let ($) f g x = f (g x) let (@@) f g = f g let id x = x let rec unfold f b = match f b with Some (a,b') -> a :: unfold f b' | _ -> [] let rec range a b = if a = b then [] else a::range (a+1) b type date = Date of int*int*int (* year * month * day_from_1900_1_1 *) let base_day = Date (1900,1,0) let days_of_month (Date (year,month,_)) = match month with 2 when (year mod 100) = 0 && (year mod 400) != 0 -> 28 | 2 when year mod 4 = 0 -> 29 | 2 -> 28 | 4|6|9|11 -> 30 | _ -> 31 let wday (Date (_,_,day)) = List.nth [`Mon;`Tue;`Wed;`Thu;`Fri;`Sat;`Sun] (day mod 7) let next_month (Date (year,month,day) as d) = let day' = day + days_of_month d in if month = 12 then Date (year+1,1,day') else Date (year,month+1,day') let next_year = List.fold_left ($) id @@ List.map (fun _->next_month) @@ range 0 12 let main () = let range_year = unfold (fun (Date (year,_,_) as date) -> if year = 2001 then None else Some (date,next_month date)) (next_year base_day) in List.length @@ List.filter (fun x -> x = `Sun) @@ List.map wday range_year let _ = if not !Sys.interactive then begin print_endline @@ string_of_int @@ main () end