From 79a441d61cd870928ddad2a94890935ed1c5f242 Mon Sep 17 00:00:00 2001 From: Gregory Date: Tue, 2 Jun 2020 02:56:53 +0300 Subject: [PATCH] up to oldest --- sml/week1/date.sml | 151 ++++++++++++++++++++++++++++++++++++--- sml/week1/date_tests.sml | 136 +++++++++++++++++++++++++++++++++++ 2 files changed, 276 insertions(+), 11 deletions(-) diff --git a/sml/week1/date.sml b/sml/week1/date.sml index c9cd278..8077bfb 100644 --- a/sml/week1/date.sml +++ b/sml/week1/date.sml @@ -1,17 +1,41 @@ (* year * month * day *) type Date = int * int * int +(* + Pipe operator. + Simply to write code like "data |> fun" instead "fun data". + Then you can "pipe" results to functions via partial application: + + fun square a b = a * b + fun sum a b = a + b + + 10 |> sum 10 |> square 2 // 40 +*) fun |> (x, f) = f x -infix |> +(* composition operator *) +fun $ (f, x) = f x -fun fold f lst acc = - case lst of - [] => acc - | head :: tail => fold f tail (f head acc) +infix |> +infix $ + +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 = - fold (fn elm => fn acc => elm :: acc) lst [] + let + fun f elm acc = elm :: acc + in + fold f [] lst + end (* non tail optimized :( *) @@ -25,18 +49,39 @@ fun reverse lst = fun filter predicate lst = let - val reversed = reverse lst fun f elm acc = if predicate elm then elm :: acc else acc in - fold f reversed [] + 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 stack :( *) +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 + 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 + if same_dates + then false + else older end fun number_in_month (dates: Date list, month_to_find: int): int = @@ -46,7 +91,7 @@ fun number_in_month (dates: Date list, month_to_find: int): int = then occurences + 1 else occurences in - fold count_month dates 0 + fold count_month 0 dates end fun number_in_months (dates: Date list, months_to_find: int list): int = @@ -54,7 +99,7 @@ fun number_in_months (dates: Date list, months_to_find: int list): int = fun count_months month acc = acc + number_in_month (dates, month) in - fold count_months months_to_find 0 + fold count_months 0 months_to_find end fun dates_in_month (dates: Date list, in_month: int): Date list = @@ -62,4 +107,88 @@ fun dates_in_month (dates: Date list, in_month: int): Date list = 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 to 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 \ No newline at end of file diff --git a/sml/week1/date_tests.sml b/sml/week1/date_tests.sml index 175798e..8711ba1 100644 --- a/sml/week1/date_tests.sml +++ b/sml/week1/date_tests.sml @@ -1,6 +1,19 @@ use "test.sml"; use "date.sml"; + +val () = + assert + (range 1 2 = [1, 2]) + "range: generates sequence in right order" + +val () = assert (sort (fn a => fn b => a > b) [] = []) "sort: empty list" +val () = assert (sort (fn a => fn b => a > b) [1] = [1]) "sort: one val" +val () = + assert + (sort (fn a => fn b => a > b) [2, 1] = [1, 2]) + "sort: two elements" + val () = assert (is_older ((1999, 12, 31), (1999, 12, 31)) = false) @@ -119,4 +132,127 @@ val () = "dates_in_month: filter non matching dates" end +val () = + let + val dates = [] + val months = [] + val expect = [] + in + assert + (dates_in_months (dates, months) = expect) + "dates_in_months: empty lists" + end + +val () = + let + val dates = [(2000, 11, 31), (2000, 12, 30), (2000, 12, 31)] + val months = [11] + val expect = [(2000, 11, 31)] + in + assert + (dates_in_months (dates, months) = expect) + "dates_in_months: match one date with one month" + end + +val () = + let + val dates = [(2000, 11, 31), (2000, 12, 30), (2000, 12, 31)] + val months = [11, 12] + val expect = dates + in + assert + (dates_in_months (dates, months) = expect) + "dates_in_months: match three dates with 2 months" + end + +val () = + assert + (get_nth (["first"], 1) = "first") + "get_nth: get first element" + +val () = + assert + (get_nth (["first", "second", "third"], 3) = "third") + "get_nth: get third element" + +val () = + assert + (get_nth (["first", "second", "third"], 4) = "") + "get_nth: not found index returns empty string :)" + +val () = + assert + (date_to_string ((2020, 06, 01)) = "May 01, 2020") + "date_to_string: returns correct string" + +val () = + assert + (number_before_reaching_sum (12, []) = 0) + "number_before_reaching_sum: empty list returns zero" + +val () = + assert + (number_before_reaching_sum (12, [1]) = 1) + "number_before_reaching_sum: one list element" + +val () = + assert + (number_before_reaching_sum (12, [4, 5, 1, 3]) = 3) + "number_before_reaching_sum: list with overflowing sum" + +val () = + assert + (number_before_reaching_sum (12, [12, 2]) = 0) + ("number_before_reaching_sum:" ^ + "first element already equals to sum but there is more") + +val () = + assert (what_month 365 = 12) "what_month: last day of a year" + +val () = + let + val day1 = 1 + val day2 = 2 + val expect = [1, 1] + in + assert + (month_range (day1, day2) = expect) + "what_month: frist 2 days of a year" + end + +val () = + let + val day1 = 31 + val day2 = 33 + val expect = [1, 2, 2] + in + assert + (month_range (day1, day2) = expect) + "what_month: three days in two different months" + end + +val () = + let + val dates = [] + val expect = NONE + in + assert (oldest dates = expect) "oldest: retruns NONE on empty list" + end + +val () = + let + val dates = [(2000, 11, 31)] + val expect = SOME (2000, 11, 31) + in + assert (oldest dates = expect) "oldest: retruns SOME date" + end + +val () = + let + val dates = [(2020, 11, 31), (2021, 11, 31)] + val expect = SOME (2020, 11, 31) + in + assert (oldest dates = expect) "oldest: retruns oldest" + end + val () = complete () \ No newline at end of file