use "operators.sml"; (* year * month * day *) type Date = int * int * int (* can't use pattern matching for now *) (* fun fold f acc lst = case lst of [] => acc | head :: tail => fold f (f head acc) tail *) fun fold f acc lst = if lst = [] then acc else let val tail = tl lst val head = hd lst in fold f (f head acc) tail end fun reverse lst = let fun f elm acc = elm :: acc in fold f [] lst end (* non tail optimized :( *) (* fun filter predicate lst = case lst of [] => lst | head :: tail => if predicate head then head :: filter predicate tail else filter predicate tail *) fun filter predicate lst = let fun f elm acc = if predicate elm then elm :: acc else acc in lst |> fold f [] |> reverse end fun range from to = let fun generate from to acc = if from > to then acc else generate (from + 1) to (from :: acc) in generate from to [] |> reverse end (* naive sort, will blow up the stack. Can't use pattern matching for now :( *) (* fun sort f lst = case lst of [] => [] | hd :: [] => [hd] | first :: second :: rest => if f first second then second :: first :: sort f rest else first :: second :: sort f rest *) (* also will blow up the stack *) fun sort f lst = if lst = [] then [] else if tl lst = [] then lst else let val first = hd lst val second = hd $ tl lst val rest = tl $ tl lst val greater = f first second in if greater then second :: first :: sort f rest else first :: second :: sort f rest end fun is_older ((y1, m1, d1): Date, (y2, m2, d2): Date): bool = let val same_dates = y1 = y2 andalso m1 = m2 andalso d1 = d2 val older = y1 <= y2 andalso m1 <= m2 andalso d1 <= d2 in if same_dates then false else older end fun number_in_month (dates: Date list, month_to_find: int): int = let fun count_month (_, month, _) occurences = if month = month_to_find then occurences + 1 else occurences in fold count_month 0 dates end fun number_in_months (dates: Date list, months_to_find: int list): int = let fun count_months month acc = acc + number_in_month (dates, month) in fold count_months 0 months_to_find end fun dates_in_month (dates: Date list, in_month: int): Date list = let fun filter_dates (_, month, _) = in_month = month in filter filter_dates dates end fun dates_in_months (dates: Date list, in_months: int list): Date list = let fun filter_dates month filtered = filtered @ dates_in_month (dates, month) in fold filter_dates [] in_months end fun get_nth (strings: string list, nth: int): string = let fun find str (pos, found) = if pos = nth then (pos + 1, str) else (pos + 1, found) in fold find (1, "") strings |> #2 end (* this function will create list for months on every call :( *) fun date_to_string ((year, month, day): Date): string = let val months = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ] val string_month = get_nth (months, month - 1) val to_str = Int.toString fun pad num = if num < 10 then "0" ^ to_str num else to_str num in string_month ^ " " ^ pad day ^ ", " ^ to_str year end fun number_before_reaching_sum (sum: int, numbers: int list): int = if numbers = [] orelse hd numbers >= sum then 0 else 1 + number_before_reaching_sum ((sum - hd numbers), tl numbers) fun what_month (day: int): int = let val months = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] in number_before_reaching_sum (day, months) + 1 end (* correct but too complex for such problem *) (* fun month_range (day1: int, day2: int): int list = let fun append_month day months = what_month day :: months in range day1 day2 |> fold append_month [] |> reverse end *) fun month_range (day1: int, day2: int): int list = if day1 > day2 then [] else what_month day1 :: month_range (day1 + 1, day2) fun oldest (dates: Date list): Date option = let fun compare a b = not $ is_older (a, b) in if dates = [] then NONE else sort compare dates |> hd |> SOME end