From a501e70592e50f5ce994da9b0709418261ba6d3e Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Fri, 9 Jun 2023 14:14:48 +0200 Subject: [PATCH 01/39] Add some solutions --- .../LuciansLusciousLasagna.fs | 12 +-- exercises/practice/tournament/Tournament.fs | 95 ++++++++++++++++++- .../practice/tournament/TournamentTests.fs | 22 ++--- 3 files changed, 108 insertions(+), 21 deletions(-) diff --git a/exercises/concept/lucians-luscious-lasagna/LuciansLusciousLasagna.fs b/exercises/concept/lucians-luscious-lasagna/LuciansLusciousLasagna.fs index 1c5bc934f..faa3e7347 100644 --- a/exercises/concept/lucians-luscious-lasagna/LuciansLusciousLasagna.fs +++ b/exercises/concept/lucians-luscious-lasagna/LuciansLusciousLasagna.fs @@ -1,9 +1,5 @@ module LuciansLusciousLasagna - -// TODO: define the 'expectedMinutesInOven' binding - -// TODO: define the 'remainingMinutesInOven' function - -// TODO: define the 'preparationTimeInMinutes' function - -// TODO: define the 'elapsedTimeInMinutes' function +let expectedMinutesInOven = 40 +let remainingMinutesInOven ovenTime = expectedMinutesInOven - ovenTime +let preparationTimeInMinutes layers = layers * 2 +let elapsedTimeInMinutes layers ovenTime = (layers * 2) + ovenTime diff --git a/exercises/practice/tournament/Tournament.fs b/exercises/practice/tournament/Tournament.fs index cbfccf968..a1e4de1f6 100644 --- a/exercises/practice/tournament/Tournament.fs +++ b/exercises/practice/tournament/Tournament.fs @@ -1,3 +1,94 @@ module Tournament - -let tally input = failwith "You need to implement this function." \ No newline at end of file + +type Outcome = + | Win + | Loss + | Draw + +type Results = + { Wins: int + Losses: int + Draws: int } + + static member Init = { Wins = 0; Losses = 0; Draws = 0 } + member this.MatchPlayed = this.Wins + this.Losses + this.Draws + member this.Points = this.Wins * 3 + this.Draws * 1 + +type Team = + { Name: string + Results: Results } + + static member Create name = { Name = name; Results = Results.Init } + +let teamScore outcome team = + let results = + match outcome with + | Win -> + { team.Results with + Wins = team.Results.Wins + 1 } + | Draw -> + { team.Results with + Draws = team.Results.Draws + 1 } + | Loss -> + { team.Results with + Losses = team.Results.Losses + 1 } + + { team with Results = results } + +let display team = + let pad input = $"{input, 3}" + $"{team.Name, -31}|{team.Results.MatchPlayed |> pad} |{team.Results.Wins |> pad} |{team.Results.Draws |> pad} |{team.Results.Losses |> pad} |{team.Results.Points |> pad}" + +let getTournamentResults (teams: Team list) = + let lines = + teams + |> List.sortBy (fun team -> -team.Results.Points, team.Name) + |> List.map display + + let header = "Team | MP | W | D | L | P" + header :: lines + +let processMatch (teams: Team list) homeTeam awayTeam outcome = + let invertOutcome = + function + | Win -> Loss + | Loss -> Win + | Draw -> Draw + + let teams = + teams + |> List.tryFind (fun t -> t.Name = homeTeam) + |> function + | None -> (Team.Create homeTeam |> teamScore outcome) :: teams + | Some team -> + let newList = teams |> List.filter (fun t -> t <> team) + let newTeam = team |> teamScore outcome + newTeam :: newList + + let teams = + teams + |> List.tryFind (fun t -> t.Name = awayTeam) + |> function + | None -> (Team.Create awayTeam |> teamScore (outcome |> invertOutcome)) :: teams + | Some team -> + let newList = teams |> List.filter (fun t -> t <> team) + let newTeam = team |> teamScore (outcome |> invertOutcome) + newTeam :: newList + + teams + +let folder (teams: Team list) (row: string) = + match row.Split(';') with + | [| home; away; outcome |] -> + let outcome = + match outcome with + | "win" -> Win + | "draw" -> Draw + | "loss" -> Loss + | _ -> failwith "Invalid outcome!" + + processMatch teams home away outcome + | _ -> teams + +let tally (input: string list) = + input |> List.fold folder list.Empty |> getTournamentResults diff --git a/exercises/practice/tournament/TournamentTests.fs b/exercises/practice/tournament/TournamentTests.fs index 1df19c46d..12c90f219 100644 --- a/exercises/practice/tournament/TournamentTests.fs +++ b/exercises/practice/tournament/TournamentTests.fs @@ -11,7 +11,7 @@ let ``Just the header if no input`` () = let expected = ["Team | MP | W | D | L | P"] tally rows |> should equal expected -[] +[] let ``A win is three points, a loss is zero points`` () = let rows = ["Allegoric Alaskans;Blithering Badgers;win"] let expected = @@ -20,7 +20,7 @@ let ``A win is three points, a loss is zero points`` () = "Blithering Badgers | 1 | 0 | 0 | 1 | 0" ] tally rows |> should equal expected -[] +[] let ``A win can also be expressed as a loss`` () = let rows = ["Blithering Badgers;Allegoric Alaskans;loss"] let expected = @@ -29,7 +29,7 @@ let ``A win can also be expressed as a loss`` () = "Blithering Badgers | 1 | 0 | 0 | 1 | 0" ] tally rows |> should equal expected -[] +[] let ``A different team can win`` () = let rows = ["Blithering Badgers;Allegoric Alaskans;win"] let expected = @@ -38,7 +38,7 @@ let ``A different team can win`` () = "Allegoric Alaskans | 1 | 0 | 0 | 1 | 0" ] tally rows |> should equal expected -[] +[] let ``A draw is one point each`` () = let rows = ["Allegoric Alaskans;Blithering Badgers;draw"] let expected = @@ -47,7 +47,7 @@ let ``A draw is one point each`` () = "Blithering Badgers | 1 | 0 | 1 | 0 | 1" ] tally rows |> should equal expected -[] +[] let ``There can be more than one match`` () = let rows = [ "Allegoric Alaskans;Blithering Badgers;win"; @@ -58,7 +58,7 @@ let ``There can be more than one match`` () = "Blithering Badgers | 2 | 0 | 0 | 2 | 0" ] tally rows |> should equal expected -[] +[] let ``There can be more than one winner`` () = let rows = [ "Allegoric Alaskans;Blithering Badgers;loss"; @@ -69,7 +69,7 @@ let ``There can be more than one winner`` () = "Blithering Badgers | 2 | 1 | 0 | 1 | 3" ] tally rows |> should equal expected -[] +[] let ``There can be more than two teams`` () = let rows = [ "Allegoric Alaskans;Blithering Badgers;win"; @@ -82,7 +82,7 @@ let ``There can be more than two teams`` () = "Courageous Californians | 2 | 0 | 0 | 2 | 0" ] tally rows |> should equal expected -[] +[] let ``Typical input`` () = let rows = [ "Allegoric Alaskans;Blithering Badgers;win"; @@ -99,7 +99,7 @@ let ``Typical input`` () = "Courageous Californians | 3 | 0 | 1 | 2 | 1" ] tally rows |> should equal expected -[] +[] let ``Incomplete competition (not all pairs have played)`` () = let rows = [ "Allegoric Alaskans;Blithering Badgers;loss"; @@ -114,7 +114,7 @@ let ``Incomplete competition (not all pairs have played)`` () = "Devastating Donkeys | 1 | 0 | 0 | 1 | 0" ] tally rows |> should equal expected -[] +[] let ``Ties broken alphabetically`` () = let rows = [ "Courageous Californians;Devastating Donkeys;win"; @@ -131,7 +131,7 @@ let ``Ties broken alphabetically`` () = "Devastating Donkeys | 3 | 0 | 1 | 2 | 1" ] tally rows |> should equal expected -[] +[] let ``Ensure points sorted numerically`` () = let rows = [ "Devastating Donkeys;Blithering Badgers;win"; From d394b18f8b49d22d71656d167cfcb60ed56ec32f Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Fri, 9 Jun 2023 14:21:50 +0200 Subject: [PATCH 02/39] Clock --- exercises/practice/clock/Clock.fs | 17 ++++- exercises/practice/clock/ClockTests.fs | 102 ++++++++++++------------- 2 files changed, 64 insertions(+), 55 deletions(-) diff --git a/exercises/practice/clock/Clock.fs b/exercises/practice/clock/Clock.fs index 121a47f97..52cf64e4b 100644 --- a/exercises/practice/clock/Clock.fs +++ b/exercises/practice/clock/Clock.fs @@ -1,9 +1,18 @@ module Clock +type Clock = { + Hours: int + Minutes: int +} -let create hours minutes = failwith "You need to implement this function." +let create hours minutes = + let m = minutes % 60 + let h = minutes / 60 + let hours = if (hours < 0) then 24 + (hours % 24) else (hours % 24) + h -let add minutes clock = failwith "You need to implement this function." + { Hours = hours; Minutes = m } -let subtract minutes clock = failwith "You need to implement this function." +let add minutes clock = create clock.Hours (clock.Minutes + minutes) -let display clock = failwith "You need to implement this function." \ No newline at end of file +let subtract minutes clock = create clock.Hours (clock.Minutes - minutes) + +let display clock = sprintf "%s:%s" (clock.Hours.ToString("00")) (clock.Minutes.ToString("00")) \ No newline at end of file diff --git a/exercises/practice/clock/ClockTests.fs b/exercises/practice/clock/ClockTests.fs index fc9a02a5d..f503a072c 100644 --- a/exercises/practice/clock/ClockTests.fs +++ b/exercises/practice/clock/ClockTests.fs @@ -10,272 +10,272 @@ let ``On the hour`` () = let clock = create 8 0 display clock |> should equal "08:00" -[] +[] let ``Past the hour`` () = let clock = create 11 9 display clock |> should equal "11:09" -[] +[] let ``Midnight is zero hours`` () = let clock = create 24 0 display clock |> should equal "00:00" -[] +[] let ``Hour rolls over`` () = let clock = create 25 0 display clock |> should equal "01:00" -[] +[] let ``Hour rolls over continuously`` () = let clock = create 100 0 display clock |> should equal "04:00" -[] +[] let ``Sixty minutes is next hour`` () = let clock = create 1 60 display clock |> should equal "02:00" -[] +[] let ``Minutes roll over`` () = let clock = create 0 160 display clock |> should equal "02:40" -[] +[] let ``Minutes roll over continuously`` () = let clock = create 0 1723 display clock |> should equal "04:43" -[] +[] let ``Hour and minutes roll over`` () = let clock = create 25 160 display clock |> should equal "03:40" -[] +[] let ``Hour and minutes roll over continuously`` () = let clock = create 201 3001 display clock |> should equal "11:01" -[] +[] let ``Hour and minutes roll over to exactly midnight`` () = let clock = create 72 8640 display clock |> should equal "00:00" -[] +[] let ``Negative hour`` () = let clock = create -1 15 display clock |> should equal "23:15" -[] +[] let ``Negative hour rolls over`` () = let clock = create -25 0 display clock |> should equal "23:00" -[] +[] let ``Negative hour rolls over continuously`` () = let clock = create -91 0 display clock |> should equal "05:00" -[] +[] let ``Negative minutes`` () = let clock = create 1 -40 display clock |> should equal "00:20" -[] +[] let ``Negative minutes roll over`` () = let clock = create 1 -160 display clock |> should equal "22:20" -[] +[] let ``Negative minutes roll over continuously`` () = let clock = create 1 -4820 display clock |> should equal "16:40" -[] +[] let ``Negative sixty minutes is previous hour`` () = let clock = create 2 -60 display clock |> should equal "01:00" -[] +[] let ``Negative hour and minutes both roll over`` () = let clock = create -25 -160 display clock |> should equal "20:20" -[] +[] let ``Negative hour and minutes both roll over continuously`` () = let clock = create -121 -5810 display clock |> should equal "22:10" -[] +[] let ``Add minutes`` () = let clock = create 10 0 add 3 clock |> display |> should equal "10:03" -[] +[] let ``Add no minutes`` () = let clock = create 6 41 add 0 clock |> display |> should equal "06:41" -[] +[] let ``Add to next hour`` () = let clock = create 0 45 add 40 clock |> display |> should equal "01:25" -[] +[] let ``Add more than one hour`` () = let clock = create 10 0 add 61 clock |> display |> should equal "11:01" -[] +[] let ``Add more than two hours with carry`` () = let clock = create 0 45 add 160 clock |> display |> should equal "03:25" -[] +[] let ``Add across midnight`` () = let clock = create 23 59 add 2 clock |> display |> should equal "00:01" -[] +[] let ``Add more than one day (1500 min = 25 hrs)`` () = let clock = create 5 32 add 1500 clock |> display |> should equal "06:32" -[] +[] let ``Add more than two days`` () = let clock = create 1 1 add 3500 clock |> display |> should equal "11:21" -[] +[] let ``Subtract minutes`` () = let clock = create 10 3 subtract 3 clock |> display |> should equal "10:00" -[] +[] let ``Subtract to previous hour`` () = let clock = create 10 3 subtract 30 clock |> display |> should equal "09:33" -[] +[] let ``Subtract more than an hour`` () = let clock = create 10 3 subtract 70 clock |> display |> should equal "08:53" -[] +[] let ``Subtract across midnight`` () = let clock = create 0 3 subtract 4 clock |> display |> should equal "23:59" -[] +[] let ``Subtract more than two hours`` () = let clock = create 0 0 subtract 160 clock |> display |> should equal "21:20" -[] +[] let ``Subtract more than two hours with borrow`` () = let clock = create 6 15 subtract 160 clock |> display |> should equal "03:35" -[] +[] let ``Subtract more than one day (1500 min = 25 hrs)`` () = let clock = create 5 32 subtract 1500 clock |> display |> should equal "04:32" -[] +[] let ``Subtract more than two days`` () = let clock = create 2 20 subtract 3000 clock |> display |> should equal "00:20" -[] +[] let ``Clocks with same time`` () = let clock1 = create 15 37 let clock2 = create 15 37 clock1 = clock2 |> should equal true -[] +[] let ``Clocks a minute apart`` () = let clock1 = create 15 36 let clock2 = create 15 37 clock1 = clock2 |> should equal false -[] +[] let ``Clocks an hour apart`` () = let clock1 = create 14 37 let clock2 = create 15 37 clock1 = clock2 |> should equal false -[] +[] let ``Clocks with hour overflow`` () = let clock1 = create 10 37 let clock2 = create 34 37 clock1 = clock2 |> should equal true -[] +[] let ``Clocks with hour overflow by several days`` () = let clock1 = create 3 11 let clock2 = create 99 11 clock1 = clock2 |> should equal true -[] +[] let ``Clocks with negative hour`` () = let clock1 = create 22 40 let clock2 = create -2 40 clock1 = clock2 |> should equal true -[] +[] let ``Clocks with negative hour that wraps`` () = let clock1 = create 17 3 let clock2 = create -31 3 clock1 = clock2 |> should equal true -[] +[] let ``Clocks with negative hour that wraps multiple times`` () = let clock1 = create 13 49 let clock2 = create -83 49 clock1 = clock2 |> should equal true -[] +[] let ``Clocks with minute overflow`` () = let clock1 = create 0 1 let clock2 = create 0 1441 clock1 = clock2 |> should equal true -[] +[] let ``Clocks with minute overflow by several days`` () = let clock1 = create 2 2 let clock2 = create 2 4322 clock1 = clock2 |> should equal true -[] +[] let ``Clocks with negative minute`` () = let clock1 = create 2 40 let clock2 = create 3 -20 clock1 = clock2 |> should equal true -[] +[] let ``Clocks with negative minute that wraps`` () = let clock1 = create 4 10 let clock2 = create 5 -1490 clock1 = clock2 |> should equal true -[] +[] let ``Clocks with negative minute that wraps multiple times`` () = let clock1 = create 6 15 let clock2 = create 6 -4305 clock1 = clock2 |> should equal true -[] +[] let ``Clocks with negative hours and minutes`` () = let clock1 = create 7 32 let clock2 = create -12 -268 clock1 = clock2 |> should equal true -[] +[] let ``Clocks with negative hours and minutes that wrap`` () = let clock1 = create 18 7 let clock2 = create -54 -11513 clock1 = clock2 |> should equal true -[] +[] let ``Full clock and zeroed clock`` () = let clock1 = create 24 0 let clock2 = create 0 0 From d8c9fb161e9207010dc8a4079f4f695f51f07cc4 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Tue, 29 Aug 2023 08:37:31 +0200 Subject: [PATCH 03/39] Minesweeper WIP --- exercises/practice/minesweeper/Minesweeper.fs | 16 +++++++++++++++- .../practice/minesweeper/MinesweeperTests.fs | 6 +++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/exercises/practice/minesweeper/Minesweeper.fs b/exercises/practice/minesweeper/Minesweeper.fs index e2858dd15..e6230af0b 100644 --- a/exercises/practice/minesweeper/Minesweeper.fs +++ b/exercises/practice/minesweeper/Minesweeper.fs @@ -1,3 +1,17 @@ module Minesweeper -let annotate input = failwith "You need to implement this function." \ No newline at end of file +let t (c:char) = + match c with + | '*' -> '*' + | _ -> ' ' + +let transform (input:string list) = + let c = input |> List.collect (fun c -> c.ToCharArray()|>Array.toList) + // printfn "%A" c + c + |> List.ta (printf "%c") + |> List.map t + |> List.chunkBySize (input |> List.length) + |> List.map(fun c -> new string (c |> List.toArray)) + +let annotate input = transform input \ No newline at end of file diff --git a/exercises/practice/minesweeper/MinesweeperTests.fs b/exercises/practice/minesweeper/MinesweeperTests.fs index 26326b079..e36995554 100644 --- a/exercises/practice/minesweeper/MinesweeperTests.fs +++ b/exercises/practice/minesweeper/MinesweeperTests.fs @@ -11,13 +11,13 @@ let ``No rows`` () = let expected: string list = [] annotate minefield |> should equal expected -[] +[] let ``No columns`` () = let minefield = [""] let expected = [""] annotate minefield |> should equal expected -[] +[] let ``No mines`` () = let minefield = [ " "; @@ -29,7 +29,7 @@ let ``No mines`` () = " " ] annotate minefield |> should equal expected -[] +[] let ``Minefield with only mines`` () = let minefield = [ "***"; From b0c3fe53b39d6dc3f6dcb93ef862389b6637bd78 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Tue, 29 Aug 2023 09:19:33 +0200 Subject: [PATCH 04/39] PhoneNumber WIP --- exercises/practice/phone-number/PhoneNumber.fs | 13 ++++++++++++- .../practice/phone-number/PhoneNumberTests.fs | 14 +++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/exercises/practice/phone-number/PhoneNumber.fs b/exercises/practice/phone-number/PhoneNumber.fs index eda6c7c81..c2fa2c1ea 100644 --- a/exercises/practice/phone-number/PhoneNumber.fs +++ b/exercises/practice/phone-number/PhoneNumber.fs @@ -1,3 +1,14 @@ module PhoneNumber -let clean input = failwith "You need to implement this function." \ No newline at end of file +let clean input : Result = + let clean oldValue (input: string) = input.Replace(oldValue, "") + let trim (input:string) = input.Trim() + + let cleaned = input |> clean "." |> clean "(" |> clean ")" |> clean "-" |> clean "+" |> clean " " |> trim + + match cleaned.Length with + | 10 -> Ok(cleaned |> uint64) + | 11 when cleaned[0] = '1' -> Ok(cleaned[1..] |> uint64) + | 11 -> Error "11 digits must start with 1" + | x when x > 11 -> Error "more than 11 digits" + | _ -> Error "incorrect number of digits" diff --git a/exercises/practice/phone-number/PhoneNumberTests.fs b/exercises/practice/phone-number/PhoneNumberTests.fs index ecc95d89b..cae8e6d4a 100644 --- a/exercises/practice/phone-number/PhoneNumberTests.fs +++ b/exercises/practice/phone-number/PhoneNumberTests.fs @@ -10,37 +10,37 @@ let ``Cleans the number`` () = let expected: Result = Ok 2234567890UL clean "(223) 456-7890" |> should equal expected -[] +[] let ``Cleans numbers with dots`` () = let expected: Result = Ok 2234567890UL clean "223.456.7890" |> should equal expected -[] +[] let ``Cleans numbers with multiple spaces`` () = let expected: Result = Ok 2234567890UL clean "223 456 7890 " |> should equal expected -[] +[] let ``Invalid when 9 digits`` () = let expected: Result = Error "incorrect number of digits" clean "123456789" |> should equal expected -[] +[] let ``Invalid when 11 digits does not start with a 1`` () = let expected: Result = Error "11 digits must start with 1" clean "22234567890" |> should equal expected -[] +[] let ``Valid when 11 digits and starting with 1`` () = let expected: Result = Ok 2234567890UL clean "12234567890" |> should equal expected -[] +[] let ``Valid when 11 digits and starting with 1 even with punctuation`` () = let expected: Result = Ok 2234567890UL clean "+1 (223) 456-7890" |> should equal expected -[] +[] let ``Invalid when more than 11 digits`` () = let expected: Result = Error "more than 11 digits" clean "321234567890" |> should equal expected From ddd2a5627b54a6b317bfe44353a282e0400f53ee Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Tue, 29 Aug 2023 13:55:20 +0200 Subject: [PATCH 05/39] Punctuations / letters --- .../practice/phone-number/PhoneNumber.fs | 37 +++++++++++++++---- .../practice/phone-number/PhoneNumberTests.fs | 4 +- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/exercises/practice/phone-number/PhoneNumber.fs b/exercises/practice/phone-number/PhoneNumber.fs index c2fa2c1ea..57fadb335 100644 --- a/exercises/practice/phone-number/PhoneNumber.fs +++ b/exercises/practice/phone-number/PhoneNumber.fs @@ -1,14 +1,35 @@ module PhoneNumber +open System + +let hasPunctuation(input: string) = + input |> Seq.exists Char.IsPunctuation + +let hasLetters(input: string) = + input |> Seq.exists Char.IsLetter + let clean input : Result = let clean oldValue (input: string) = input.Replace(oldValue, "") - let trim (input:string) = input.Trim() + let trim(input: string) = input.Trim() - let cleaned = input |> clean "." |> clean "(" |> clean ")" |> clean "-" |> clean "+" |> clean " " |> trim + let cleaned = + input + |> clean "." + |> clean "(" + |> clean ")" + |> clean "-" + |> clean "+" + |> clean " " + |> trim - match cleaned.Length with - | 10 -> Ok(cleaned |> uint64) - | 11 when cleaned[0] = '1' -> Ok(cleaned[1..] |> uint64) - | 11 -> Error "11 digits must start with 1" - | x when x > 11 -> Error "more than 11 digits" - | _ -> Error "incorrect number of digits" + if (cleaned |> hasLetters) then + Error "letters not permitted" + elif (cleaned |> hasPunctuation) then + Error "punctuations not permitted" + else + match cleaned.Length with + | 10 -> Ok(cleaned |> uint64) + | 11 when cleaned[0] = '1' -> Ok(cleaned[1..] |> uint64) + | 11 -> Error "11 digits must start with 1" + | x when x > 11 -> Error "more than 11 digits" + | _ -> Error "incorrect number of digits" diff --git a/exercises/practice/phone-number/PhoneNumberTests.fs b/exercises/practice/phone-number/PhoneNumberTests.fs index cae8e6d4a..2733e4db3 100644 --- a/exercises/practice/phone-number/PhoneNumberTests.fs +++ b/exercises/practice/phone-number/PhoneNumberTests.fs @@ -45,12 +45,12 @@ let ``Invalid when more than 11 digits`` () = let expected: Result = Error "more than 11 digits" clean "321234567890" |> should equal expected -[] +[] let ``Invalid with letters`` () = let expected: Result = Error "letters not permitted" clean "523-abc-7890" |> should equal expected -[] +[] let ``Invalid with punctuations`` () = let expected: Result = Error "punctuations not permitted" clean "523-@:!-7890" |> should equal expected From 0c9bc6ae99679d745d7ad7ec454d5f7ce1a4824b Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Tue, 29 Aug 2023 15:39:41 +0200 Subject: [PATCH 06/39] Refacto phone number --- .../practice/phone-number/PhoneNumber.fs | 50 ++++++++++++------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/exercises/practice/phone-number/PhoneNumber.fs b/exercises/practice/phone-number/PhoneNumber.fs index 57fadb335..39dabd2db 100644 --- a/exercises/practice/phone-number/PhoneNumber.fs +++ b/exercises/practice/phone-number/PhoneNumber.fs @@ -2,18 +2,23 @@ module PhoneNumber open System -let hasPunctuation(input: string) = - input |> Seq.exists Char.IsPunctuation +let validatePunctuation(input: string) = + if input |> Seq.exists Char.IsPunctuation then + Error "punctuations not permitted" + else + Ok input -let hasLetters(input: string) = - input |> Seq.exists Char.IsLetter +let validateLetters(input: string) = + if input |> Seq.exists Char.IsLetter then + Error "letters not permitted" + else + Ok input -let clean input : Result = +let cleanNumber (input:string) = let clean oldValue (input: string) = input.Replace(oldValue, "") let trim(input: string) = input.Trim() - let cleaned = - input + input |> clean "." |> clean "(" |> clean ")" @@ -21,15 +26,24 @@ let clean input : Result = |> clean "+" |> clean " " |> trim + +let validate(cleaned: string) = + match cleaned.Length with + | 10 -> Ok(cleaned |> uint64) + | 11 when cleaned[0] = '1' -> Ok(cleaned[1..] |> uint64) + | 11 -> Error "11 digits must start with 1" + | x when x > 11 -> Error "more than 11 digits" + | _ -> Error "incorrect number of digits" - if (cleaned |> hasLetters) then - Error "letters not permitted" - elif (cleaned |> hasPunctuation) then - Error "punctuations not permitted" - else - match cleaned.Length with - | 10 -> Ok(cleaned |> uint64) - | 11 when cleaned[0] = '1' -> Ok(cleaned[1..] |> uint64) - | 11 -> Error "11 digits must start with 1" - | x when x > 11 -> Error "more than 11 digits" - | _ -> Error "incorrect number of digits" +let clean input : Result = + let x = input |> cleanNumber |> validateLetters |> Result.bind validatePunctuation |> Result.bind validate + + x + +// else +// match cleaned.Length with +// | 10 -> Ok(cleaned |> uint64) +// | 11 when cleaned[0] = '1' -> Ok(cleaned[1..] |> uint64) +// | 11 -> Error "11 digits must start with 1" +// | x when x > 11 -> Error "more than 11 digits" +// | _ -> Error "incorrect number of digits" From 6b56b614245813dbd7f092658eea31abb69f7f09 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Wed, 30 Aug 2023 13:09:49 +0200 Subject: [PATCH 07/39] Phone number solved --- .../practice/phone-number/PhoneNumber.fs | 65 ++++++++++++------- .../practice/phone-number/PhoneNumberTests.fs | 16 ++--- 2 files changed, 50 insertions(+), 31 deletions(-) diff --git a/exercises/practice/phone-number/PhoneNumber.fs b/exercises/practice/phone-number/PhoneNumber.fs index 39dabd2db..51f824744 100644 --- a/exercises/practice/phone-number/PhoneNumber.fs +++ b/exercises/practice/phone-number/PhoneNumber.fs @@ -14,36 +14,55 @@ let validateLetters(input: string) = else Ok input -let cleanNumber (input:string) = +let cleanNumber(input: string) = let clean oldValue (input: string) = input.Replace(oldValue, "") let trim(input: string) = input.Trim() input - |> clean "." - |> clean "(" - |> clean ")" - |> clean "-" - |> clean "+" - |> clean " " - |> trim - -let validate(cleaned: string) = + |> clean "." + |> clean "(" + |> clean ")" + |> clean "-" + |> clean "+" + |> clean " " + |> trim + +let validateLength(cleaned: string) = match cleaned.Length with - | 10 -> Ok(cleaned |> uint64) - | 11 when cleaned[0] = '1' -> Ok(cleaned[1..] |> uint64) - | 11 -> Error "11 digits must start with 1" + | 10 + | 11 -> Ok(cleaned) | x when x > 11 -> Error "more than 11 digits" | _ -> Error "incorrect number of digits" -let clean input : Result = - let x = input |> cleanNumber |> validateLetters |> Result.bind validatePunctuation |> Result.bind validate +let validateCountryCode(input: string) = + match input.Length, input[0] with + | 10, _ -> Ok input + | 11, '1' -> Ok input + | _ -> Error "11 digits must start with 1" + +let removeCountryCode(input: string) = + if (input.Length = 11) then input[1..] else input - x +let validateAreaCode(input: string) = + match input[0] with + | '0' -> Error "area code cannot start with zero" + | '1' -> Error "area code cannot start with one" + | _ -> Ok input -// else -// match cleaned.Length with -// | 10 -> Ok(cleaned |> uint64) -// | 11 when cleaned[0] = '1' -> Ok(cleaned[1..] |> uint64) -// | 11 -> Error "11 digits must start with 1" -// | x when x > 11 -> Error "more than 11 digits" -// | _ -> Error "incorrect number of digits" +let validateExchangeCode(input: string) = + match input[3] with + | '0' -> Error "exchange code cannot start with zero" + | '1' -> Error "exchange code cannot start with one" + | _ -> Ok input + +let clean input : Result = + input + |> cleanNumber + |> validateLetters + |> Result.bind validatePunctuation + |> Result.bind validateLength + |> Result.bind validateCountryCode + |> Result.map removeCountryCode + |> Result.bind validateAreaCode + |> Result.bind validateExchangeCode + |> Result.map uint64 diff --git a/exercises/practice/phone-number/PhoneNumberTests.fs b/exercises/practice/phone-number/PhoneNumberTests.fs index 2733e4db3..6393a0ccc 100644 --- a/exercises/practice/phone-number/PhoneNumberTests.fs +++ b/exercises/practice/phone-number/PhoneNumberTests.fs @@ -55,42 +55,42 @@ let ``Invalid with punctuations`` () = let expected: Result = Error "punctuations not permitted" clean "523-@:!-7890" |> should equal expected -[] +[] let ``Invalid if area code starts with 0`` () = let expected: Result = Error "area code cannot start with zero" clean "(023) 456-7890" |> should equal expected -[] +[] let ``Invalid if area code starts with 1`` () = let expected: Result = Error "area code cannot start with one" clean "(123) 456-7890" |> should equal expected -[] +[] let ``Invalid if exchange code starts with 0`` () = let expected: Result = Error "exchange code cannot start with zero" clean "(223) 056-7890" |> should equal expected -[] +[] let ``Invalid if exchange code starts with 1`` () = let expected: Result = Error "exchange code cannot start with one" clean "(223) 156-7890" |> should equal expected -[] +[] let ``Invalid if area code starts with 0 on valid 11-digit number`` () = let expected: Result = Error "area code cannot start with zero" clean "1 (023) 456-7890" |> should equal expected -[] +[] let ``Invalid if area code starts with 1 on valid 11-digit number`` () = let expected: Result = Error "area code cannot start with one" clean "1 (123) 456-7890" |> should equal expected -[] +[] let ``Invalid if exchange code starts with 0 on valid 11-digit number`` () = let expected: Result = Error "exchange code cannot start with zero" clean "1 (223) 056-7890" |> should equal expected -[] +[] let ``Invalid if exchange code starts with 1 on valid 11-digit number`` () = let expected: Result = Error "exchange code cannot start with one" clean "1 (223) 156-7890" |> should equal expected From 4446c3a19cdd12a0996e2e7ec6fb2e5a306c2a03 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Wed, 30 Aug 2023 16:23:22 +0200 Subject: [PATCH 08/39] Tournament: advice 1 --- exercises/practice/tournament/Tournament.fs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/exercises/practice/tournament/Tournament.fs b/exercises/practice/tournament/Tournament.fs index a1e4de1f6..1ce756498 100644 --- a/exercises/practice/tournament/Tournament.fs +++ b/exercises/practice/tournament/Tournament.fs @@ -39,7 +39,7 @@ let display team = let pad input = $"{input, 3}" $"{team.Name, -31}|{team.Results.MatchPlayed |> pad} |{team.Results.Wins |> pad} |{team.Results.Draws |> pad} |{team.Results.Losses |> pad} |{team.Results.Points |> pad}" -let getTournamentResults (teams: Team list) = +let getTournamentResults(teams: Team list) = let lines = teams |> List.sortBy (fun team -> -team.Results.Points, team.Name) @@ -60,10 +60,7 @@ let processMatch (teams: Team list) homeTeam awayTeam outcome = |> List.tryFind (fun t -> t.Name = homeTeam) |> function | None -> (Team.Create homeTeam |> teamScore outcome) :: teams - | Some team -> - let newList = teams |> List.filter (fun t -> t <> team) - let newTeam = team |> teamScore outcome - newTeam :: newList + | Some team -> teams |> List.map (fun t -> if t = team then team |> teamScore outcome else t) let teams = teams @@ -71,10 +68,12 @@ let processMatch (teams: Team list) homeTeam awayTeam outcome = |> function | None -> (Team.Create awayTeam |> teamScore (outcome |> invertOutcome)) :: teams | Some team -> - let newList = teams |> List.filter (fun t -> t <> team) - let newTeam = team |> teamScore (outcome |> invertOutcome) - newTeam :: newList - + teams + |> List.map (fun t -> + if t = team then + team |> teamScore (outcome |> invertOutcome) + else + t) teams let folder (teams: Team list) (row: string) = @@ -90,5 +89,5 @@ let folder (teams: Team list) (row: string) = processMatch teams home away outcome | _ -> teams -let tally (input: string list) = +let tally(input: string list) = input |> List.fold folder list.Empty |> getTournamentResults From 95e76ff49537273070eac845e20e4b2d75ea8dd2 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Wed, 30 Aug 2023 17:06:53 +0200 Subject: [PATCH 09/39] Refacto \o/ --- exercises/practice/tournament/Tournament.fs | 65 +++++++++------------ 1 file changed, 26 insertions(+), 39 deletions(-) diff --git a/exercises/practice/tournament/Tournament.fs b/exercises/practice/tournament/Tournament.fs index 1ce756498..34ee67fdb 100644 --- a/exercises/practice/tournament/Tournament.fs +++ b/exercises/practice/tournament/Tournament.fs @@ -5,6 +5,12 @@ type Outcome = | Loss | Draw + static member Invert = + function + | Win -> Loss + | Loss -> Win + | Draw -> Draw + type Results = { Wins: int Losses: int @@ -20,20 +26,15 @@ type Team = static member Create name = { Name = name; Results = Results.Init } -let teamScore outcome team = - let results = - match outcome with - | Win -> - { team.Results with - Wins = team.Results.Wins + 1 } - | Draw -> - { team.Results with - Draws = team.Results.Draws + 1 } - | Loss -> - { team.Results with - Losses = team.Results.Losses + 1 } - - { team with Results = results } +let addOutcome outcome results = + match outcome with + | Win -> { results with Wins = results.Wins + 1 } + | Draw -> + { results with + Draws = results.Draws + 1 } + | Loss -> + { results with + Losses = results.Losses + 1 } let display team = let pad input = $"{input, 3}" @@ -49,32 +50,18 @@ let getTournamentResults(teams: Team list) = header :: lines let processMatch (teams: Team list) homeTeam awayTeam outcome = - let invertOutcome = - function - | Win -> Loss - | Loss -> Win - | Draw -> Draw - - let teams = - teams - |> List.tryFind (fun t -> t.Name = homeTeam) - |> function - | None -> (Team.Create homeTeam |> teamScore outcome) :: teams - | Some team -> teams |> List.map (fun t -> if t = team then team |> teamScore outcome else t) - - let teams = - teams - |> List.tryFind (fun t -> t.Name = awayTeam) - |> function - | None -> (Team.Create awayTeam |> teamScore (outcome |> invertOutcome)) :: teams - | Some team -> - teams - |> List.map (fun t -> - if t = team then - team |> teamScore (outcome |> invertOutcome) - else - t) + let addResult teamName outcome teams = + match (teams |> Map.tryFind teamName) with + | Some results -> teams.Add(teamName, addOutcome outcome results) + | None -> teams.Add(teamName, addOutcome outcome Results.Init) + teams + |> List.map (fun t -> t.Name, t.Results) + |> Map.ofList + |> addResult homeTeam outcome + |> addResult awayTeam (outcome |> Outcome.Invert) + |> Map.toList + |> List.map (fun (name, results) -> { Name = name; Results = results }) let folder (teams: Team list) (row: string) = match row.Split(';') with From 6ee45b9467acc313d1c9a359f52833ee1c99aee0 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Wed, 30 Aug 2023 19:39:10 +0200 Subject: [PATCH 10/39] Tournament : small changes --- exercises/practice/tournament/Tournament.fs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/exercises/practice/tournament/Tournament.fs b/exercises/practice/tournament/Tournament.fs index 34ee67fdb..f54b64d95 100644 --- a/exercises/practice/tournament/Tournament.fs +++ b/exercises/practice/tournament/Tournament.fs @@ -24,8 +24,6 @@ type Team = { Name: string Results: Results } - static member Create name = { Name = name; Results = Results.Init } - let addOutcome outcome results = match outcome with | Win -> { results with Wins = results.Wins + 1 } @@ -49,21 +47,21 @@ let getTournamentResults(teams: Team list) = let header = "Team | MP | W | D | L | P" header :: lines -let processMatch (teams: Team list) homeTeam awayTeam outcome = +let processMatch teams homeTeam awayTeam outcome = let addResult teamName outcome teams = match (teams |> Map.tryFind teamName) with | Some results -> teams.Add(teamName, addOutcome outcome results) | None -> teams.Add(teamName, addOutcome outcome Results.Init) teams - |> List.map (fun t -> t.Name, t.Results) + |> List.map (fun team -> team.Name, team.Results) |> Map.ofList |> addResult homeTeam outcome |> addResult awayTeam (outcome |> Outcome.Invert) |> Map.toList |> List.map (fun (name, results) -> { Name = name; Results = results }) -let folder (teams: Team list) (row: string) = +let folder teams (row: string) = match row.Split(';') with | [| home; away; outcome |] -> let outcome = From 4a4dddae6a93fed5dbde666917ff3314ee658c79 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Tue, 5 Sep 2023 16:10:50 +0200 Subject: [PATCH 11/39] Refactor Telephone number --- .../practice/phone-number/PhoneNumber.fs | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/exercises/practice/phone-number/PhoneNumber.fs b/exercises/practice/phone-number/PhoneNumber.fs index 51f824744..447178c32 100644 --- a/exercises/practice/phone-number/PhoneNumber.fs +++ b/exercises/practice/phone-number/PhoneNumber.fs @@ -34,25 +34,27 @@ let validateLength(cleaned: string) = | x when x > 11 -> Error "more than 11 digits" | _ -> Error "incorrect number of digits" -let validateCountryCode(input: string) = - match input.Length, input[0] with +let validateCountryCode(input: char array) = + match input.Length, (input |> Array.tryHead) with | 10, _ -> Ok input - | 11, '1' -> Ok input + | 11, Some '1' -> Ok input | _ -> Error "11 digits must start with 1" -let removeCountryCode(input: string) = +let removeCountryCode(input: char array) = if (input.Length = 11) then input[1..] else input -let validateAreaCode(input: string) = - match input[0] with - | '0' -> Error "area code cannot start with zero" - | '1' -> Error "area code cannot start with one" +let validateAreaCode(input: char array) = + match Array.tryHead input with + | Some '0' -> Error "area code cannot start with zero" + | Some '1' -> Error "area code cannot start with one" + | None -> Error "Empty phone!" | _ -> Ok input -let validateExchangeCode(input: string) = - match input[3] with - | '0' -> Error "exchange code cannot start with zero" - | '1' -> Error "exchange code cannot start with one" +let validateExchangeCode(input: char array) = + match input |> Array.tryItem 3 with + | Some '0' -> Error "exchange code cannot start with zero" + | Some '1' -> Error "exchange code cannot start with one" + | None -> Error "Invalid phone!" | _ -> Ok input let clean input : Result = @@ -61,8 +63,9 @@ let clean input : Result = |> validateLetters |> Result.bind validatePunctuation |> Result.bind validateLength + |> Result.map (fun s -> s.ToCharArray()) |> Result.bind validateCountryCode |> Result.map removeCountryCode |> Result.bind validateAreaCode |> Result.bind validateExchangeCode - |> Result.map uint64 + |> Result.map (fun array -> new string(array) |> uint64) From cafb07b73083744b0289d65b8c581c94738306ad Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Wed, 6 Sep 2023 13:35:48 +0200 Subject: [PATCH 12/39] Start of pig latin --- exercises/practice/pig-latin/PigLatin.fs | 11 ++++++++++- exercises/practice/pig-latin/PigLatinTests.fs | 12 ++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/exercises/practice/pig-latin/PigLatin.fs b/exercises/practice/pig-latin/PigLatin.fs index 51297c849..cc957417f 100644 --- a/exercises/practice/pig-latin/PigLatin.fs +++ b/exercises/practice/pig-latin/PigLatin.fs @@ -1,3 +1,12 @@ module PigLatin -let translate input = failwith "You need to implement this function." \ No newline at end of file +let isVowelSound (c:char) = + match c with + | 'a' | 'e' | 'i' | 'o' | 'u' -> true + | _ -> false + +let translate (input:string) = + match (Seq.tryHead input) with + | Some c when isVowelSound c -> input + "ay" + | Some _ -> input + | None -> input \ No newline at end of file diff --git a/exercises/practice/pig-latin/PigLatinTests.fs b/exercises/practice/pig-latin/PigLatinTests.fs index eb5bfa733..6084bb875 100644 --- a/exercises/practice/pig-latin/PigLatinTests.fs +++ b/exercises/practice/pig-latin/PigLatinTests.fs @@ -9,27 +9,27 @@ open PigLatin let ``Word beginning with a`` () = translate "apple" |> should equal "appleay" -[] +[] let ``Word beginning with e`` () = translate "ear" |> should equal "earay" -[] +[] let ``Word beginning with i`` () = translate "igloo" |> should equal "iglooay" -[] +[] let ``Word beginning with o`` () = translate "object" |> should equal "objectay" -[] +[] let ``Word beginning with u`` () = translate "under" |> should equal "underay" -[] +[] let ``Word beginning with a vowel and followed by a qu`` () = translate "equal" |> should equal "equalay" -[] +[] let ``Word beginning with p`` () = translate "pig" |> should equal "igpay" From 6b8ee79759520e6140031bf41845d298ade8f520 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Thu, 14 Dec 2023 09:36:05 +0100 Subject: [PATCH 13/39] Solve Minesweeper. --- exercises/practice/minesweeper/Minesweeper.fs | 65 ++++++++++++++----- .../practice/minesweeper/MinesweeperTests.fs | 16 ++--- 2 files changed, 58 insertions(+), 23 deletions(-) diff --git a/exercises/practice/minesweeper/Minesweeper.fs b/exercises/practice/minesweeper/Minesweeper.fs index e6230af0b..80e6d4721 100644 --- a/exercises/practice/minesweeper/Minesweeper.fs +++ b/exercises/practice/minesweeper/Minesweeper.fs @@ -1,17 +1,52 @@ module Minesweeper -let t (c:char) = - match c with - | '*' -> '*' - | _ -> ' ' - -let transform (input:string list) = - let c = input |> List.collect (fun c -> c.ToCharArray()|>Array.toList) - // printfn "%A" c - c - |> List.ta (printf "%c") - |> List.map t - |> List.chunkBySize (input |> List.length) - |> List.map(fun c -> new string (c |> List.toArray)) - -let annotate input = transform input \ No newline at end of file +type Type = + | Empty + | Mine + +type Position = int * int + +let parseChar = + function + | ' ' -> Empty + | '*' -> Mine + | _ -> failwith "Unexpected char" + +let calculateMineCount minePositions element = + let (x, y), type' = element + + match type' with + | Mine -> "*" + | Empty -> + let neighbours = + [ x - 1, y - 1 + x, y - 1 + x + 1, y - 1 + x - 1, y + x + 1, y + x - 1, y + 1 + x, y + 1 + x + 1, y + 1 ] + |> Set.ofList + + let count = Set.intersect neighbours (minePositions |> Set.ofList) |> Set.count + if count = 0 then " " else (count |> string) + +let transform minePositions element = + element |> List.map (calculateMineCount minePositions) |> String.concat "" + +let annotate(input: string list) = + let field = + [ for row, line in (input |> List.indexed) do + [ for col, char in (line |> Seq.indexed) -> (row, col), parseChar char ] ] + + let minePositions = + field + |> List.collect (fun element -> + element + |> List.choose (fun (pos, type') -> + match type' with + | Empty -> None + | Mine -> Some pos)) + + field |> List.map (transform minePositions) diff --git a/exercises/practice/minesweeper/MinesweeperTests.fs b/exercises/practice/minesweeper/MinesweeperTests.fs index e36995554..bd2005eb7 100644 --- a/exercises/practice/minesweeper/MinesweeperTests.fs +++ b/exercises/practice/minesweeper/MinesweeperTests.fs @@ -41,7 +41,7 @@ let ``Minefield with only mines`` () = "***" ] annotate minefield |> should equal expected -[] +[] let ``Mine surrounded by spaces`` () = let minefield = [ " "; @@ -53,7 +53,7 @@ let ``Mine surrounded by spaces`` () = "111" ] annotate minefield |> should equal expected -[] +[] let ``Space surrounded by mines`` () = let minefield = [ "***"; @@ -65,19 +65,19 @@ let ``Space surrounded by mines`` () = "***" ] annotate minefield |> should equal expected -[] +[] let ``Horizontal line`` () = let minefield = [" * * "] let expected = ["1*2*1"] annotate minefield |> should equal expected -[] +[] let ``Horizontal line, mines at edges`` () = let minefield = ["* *"] let expected = ["*1 1*"] annotate minefield |> should equal expected -[] +[] let ``Vertical line`` () = let minefield = [ " "; @@ -93,7 +93,7 @@ let ``Vertical line`` () = "1" ] annotate minefield |> should equal expected -[] +[] let ``Vertical line, mines at edges`` () = let minefield = [ "*"; @@ -109,7 +109,7 @@ let ``Vertical line, mines at edges`` () = "*" ] annotate minefield |> should equal expected -[] +[] let ``Cross`` () = let minefield = [ " * "; @@ -125,7 +125,7 @@ let ``Cross`` () = " 2*2 " ] annotate minefield |> should equal expected -[] +[] let ``Large minefield`` () = let minefield = [ " * * "; From c49207f610403c8739e3461705c9d6aa77f8f33a Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Thu, 14 Dec 2023 15:25:54 +0100 Subject: [PATCH 14/39] RLE encode --- .../run-length-encoding/RunLengthEncoding.fs | 19 +++++++++++++++++-- .../RunLengthEncodingTests.fs | 8 ++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/exercises/practice/run-length-encoding/RunLengthEncoding.fs b/exercises/practice/run-length-encoding/RunLengthEncoding.fs index 90ccb8976..525dd919a 100644 --- a/exercises/practice/run-length-encoding/RunLengthEncoding.fs +++ b/exercises/practice/run-length-encoding/RunLengthEncoding.fs @@ -1,5 +1,20 @@ module RunLengthEncoding -let encode input = failwith "You need to implement this function." +let rec rle input = + match input with + | [] -> [] + | h :: _ -> + let sequence = input |> Seq.takeWhile (fun c -> c = h) + let length = sequence |> Seq.length + let entry = if length = 1 then $"{h}" else $"{length}{h}" + let rest = input |> List.skip length + entry :: (rle rest) -let decode input = failwith "You need to implement this function." \ No newline at end of file +let encode(input: string) = + input.ToCharArray() |> Array.toList |> rle |> String.concat "" + +let decode input = + failwith "You need to implement this function." + +encode "AABBBCCCC" +encode "aabbbcccc" diff --git a/exercises/practice/run-length-encoding/RunLengthEncodingTests.fs b/exercises/practice/run-length-encoding/RunLengthEncodingTests.fs index a8c760d21..85cbbbc52 100644 --- a/exercises/practice/run-length-encoding/RunLengthEncodingTests.fs +++ b/exercises/practice/run-length-encoding/RunLengthEncodingTests.fs @@ -9,7 +9,7 @@ open RunLengthEncoding let ``Encode empty string`` () = encode "" |> should equal "" -[] +[] let ``Encode single characters only are encoded without count`` () = encode "XYZ" |> should equal "XYZ" @@ -17,15 +17,15 @@ let ``Encode single characters only are encoded without count`` () = let ``Encode string with no single characters`` () = encode "AABBBCCCC" |> should equal "2A3B4C" -[] +[] let ``Encode single characters mixed with repeated characters`` () = encode "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB" |> should equal "12WB12W3B24WB" -[] +[] let ``Encode multiple whitespace mixed in string`` () = encode " hsqq qww " |> should equal "2 hs2q q2w2 " -[] +[] let ``Encode lowercase characters`` () = encode "aabbbcccc" |> should equal "2a3b4c" From a000d80ccb6fd6f3d5789a5fef3efa3047222796 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Thu, 14 Dec 2023 17:09:22 +0100 Subject: [PATCH 15/39] RLE : almost decode --- .../run-length-encoding/RunLengthEncoding.fs | 33 ++++++++++++++----- .../RunLengthEncodingTests.fs | 16 ++++----- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/exercises/practice/run-length-encoding/RunLengthEncoding.fs b/exercises/practice/run-length-encoding/RunLengthEncoding.fs index 525dd919a..907479f01 100644 --- a/exercises/practice/run-length-encoding/RunLengthEncoding.fs +++ b/exercises/practice/run-length-encoding/RunLengthEncoding.fs @@ -1,20 +1,35 @@ module RunLengthEncoding +open System + let rec rle input = match input with - | [] -> [] + | [] -> "" | h :: _ -> let sequence = input |> Seq.takeWhile (fun c -> c = h) let length = sequence |> Seq.length let entry = if length = 1 then $"{h}" else $"{length}{h}" let rest = input |> List.skip length - entry :: (rle rest) - -let encode(input: string) = - input.ToCharArray() |> Array.toList |> rle |> String.concat "" + entry + (rle rest) -let decode input = - failwith "You need to implement this function." +let encode (input:string) = + input.ToCharArray() |> Array.toList |> rle -encode "AABBBCCCC" -encode "aabbbcccc" +let rec rled input = + match input with + | [] -> "" + | h :: t when Char.IsDigit h -> + let count = h |> string |> int + let char = t |> Seq.head + new string(char, count) + (rled t[1..]) + // let count = input |> Seq.takeWhile Char.IsDigit + // let number = String.Join("", count) |> int + // let char = t |> Seq.skip (count |> Seq.length) |> Seq.head + // new string(char, number) + (rled t[1..]) + | h :: t -> + (h |> string) + (rled t) + +let decode (input:string) = + input.ToCharArray() |> Array.toList |> rled + +decode "12WB12W3B24WB" \ No newline at end of file diff --git a/exercises/practice/run-length-encoding/RunLengthEncodingTests.fs b/exercises/practice/run-length-encoding/RunLengthEncodingTests.fs index 85cbbbc52..cb90d1614 100644 --- a/exercises/practice/run-length-encoding/RunLengthEncodingTests.fs +++ b/exercises/practice/run-length-encoding/RunLengthEncodingTests.fs @@ -13,7 +13,7 @@ let ``Encode empty string`` () = let ``Encode single characters only are encoded without count`` () = encode "XYZ" |> should equal "XYZ" -[] +[] let ``Encode string with no single characters`` () = encode "AABBBCCCC" |> should equal "2A3B4C" @@ -29,31 +29,31 @@ let ``Encode multiple whitespace mixed in string`` () = let ``Encode lowercase characters`` () = encode "aabbbcccc" |> should equal "2a3b4c" -[] +[] let ``Decode empty string`` () = decode "" |> should equal "" -[] +[] let ``Decode single characters only`` () = decode "XYZ" |> should equal "XYZ" -[] +[] let ``Decode string with no single characters`` () = decode "2A3B4C" |> should equal "AABBBCCCC" -[] +[] let ``Decode single characters with repeated characters`` () = decode "12WB12W3B24WB" |> should equal "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB" -[] +[] let ``Decode multiple whitespace mixed in string`` () = decode "2 hs2q q2w2 " |> should equal " hsqq qww " -[] +[] let ``Decode lowercase string`` () = decode "2a3b4c" |> should equal "aabbbcccc" -[] +[] let ``Encode followed by decode gives original string`` () = "zzz ZZ zZ" |> encode |> decode |> should equal "zzz ZZ zZ" From 81547cefa01eec94aa9aaac2b0d09d7d301a60b7 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Mon, 18 Dec 2023 13:35:22 +0100 Subject: [PATCH 16/39] Solve RLE --- .../run-length-encoding/RunLengthEncoding.fs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/exercises/practice/run-length-encoding/RunLengthEncoding.fs b/exercises/practice/run-length-encoding/RunLengthEncoding.fs index 907479f01..53987b667 100644 --- a/exercises/practice/run-length-encoding/RunLengthEncoding.fs +++ b/exercises/practice/run-length-encoding/RunLengthEncoding.fs @@ -18,18 +18,17 @@ let encode (input:string) = let rec rled input = match input with | [] -> "" - | h :: t when Char.IsDigit h -> - let count = h |> string |> int - let char = t |> Seq.head - new string(char, count) + (rled t[1..]) - // let count = input |> Seq.takeWhile Char.IsDigit - // let number = String.Join("", count) |> int - // let char = t |> Seq.skip (count |> Seq.length) |> Seq.head - // new string(char, number) + (rled t[1..]) + | h :: _ when Char.IsDigit h -> + let ar = input |> Seq.takeWhile Char.IsDigit |> Seq.toArray + let count = new string(ar) |> int + let len = ar |> Array.length + let char = input[len..len] |> List.head + + let rest = input |> List.skip (ar.Length + 1) + + new string(char, count) + (rled rest) | h :: t -> (h |> string) + (rled t) let decode (input:string) = input.ToCharArray() |> Array.toList |> rled - -decode "12WB12W3B24WB" \ No newline at end of file From cdfc7260e778e04fcf3506a53c2c133b920c6dd0 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Tue, 9 Jan 2024 16:57:39 +0100 Subject: [PATCH 17/39] Poker: parse hands --- exercises/practice/poker/Poker.fs | 54 +++++++++++++++++++++++++- exercises/practice/poker/PokerTests.fs | 2 +- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/exercises/practice/poker/Poker.fs b/exercises/practice/poker/Poker.fs index 1fb0f7196..163e6684b 100644 --- a/exercises/practice/poker/Poker.fs +++ b/exercises/practice/poker/Poker.fs @@ -1,3 +1,55 @@ module Poker -// TODO: implement this module \ No newline at end of file +open System.Text.RegularExpressions + +type Color = + | Hearts + | Spades + | Diamonds + | Clubs + +type Type = + | As + | King + | Queen + | Jack + | Value of int + +type Card = { Card: Type * Color } + +type Hand = Card array + +let makeCard(card: string) : Card = + let m = Regex.Match(card, @"(?\d{1,2}|J|Q|K|A)(?\w)") + let type' = + match m.Groups["type"].Value with + | "A" -> As + | "K" -> King + | "Q" -> Queen + | "J" -> Jack + | v -> Value(v |> string |> int) + + let color = + match m.Groups["color"].Value with + | "H" -> Hearts + | "D" -> Diamonds + | "S" -> Spades + | "C" -> Clubs + | _ -> failwith "Unknown color" + + { Card = type', color } + +let makeHand(hand: string) : Hand = + hand.Split(' ') |> Array.map makeCard + +let bestHands(hands: string list) = + let pokerHands = hands |> List.map makeHand + hands[0] + +let s2 = "4S 5S 7H 8D JC" + +let m = Regex.Match("4S 5S 7H 8D JC", "(\d{1,2}|J|Q|K|A)(\w)") +let hands = ["4D 5S 6S 8D 3C"; "2S 4C 7S 9H 10H"; "3S 4S 5D 6H JH"] + +hands |> List.map makeHand +let x = bestHands hands diff --git a/exercises/practice/poker/PokerTests.fs b/exercises/practice/poker/PokerTests.fs index 5d7d7a6ab..2ce276316 100644 --- a/exercises/practice/poker/PokerTests.fs +++ b/exercises/practice/poker/PokerTests.fs @@ -11,7 +11,7 @@ let ``Single hand always wins`` () = let expected = ["4S 5S 7H 8D JC"] bestHands hands |> should equal expected -[] +[] let ``Highest card out of all hands wins`` () = let hands = ["4D 5S 6S 8D 3C"; "2S 4C 7S 9H 10H"; "3S 4S 5D 6H JH"] let expected = ["3S 4S 5D 6H JH"] From f98892502538a0fdf97ffc3a1cbcd6e63a080ee0 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Fri, 12 Jan 2024 16:09:46 +0100 Subject: [PATCH 18/39] Almost NucleotideCount.fs --- .../practice/nucleotide-count/NucleotideCount.fs | 16 +++++++++++++++- .../nucleotide-count/NucleotideCountTests.fs | 8 ++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/exercises/practice/nucleotide-count/NucleotideCount.fs b/exercises/practice/nucleotide-count/NucleotideCount.fs index b1136f923..a26f75c9a 100644 --- a/exercises/practice/nucleotide-count/NucleotideCount.fs +++ b/exercises/practice/nucleotide-count/NucleotideCount.fs @@ -1,3 +1,17 @@ module NucleotideCount -let nucleotideCounts (strand: string): Option> = failwith "You need to implement this function." +let folder map (c: char) = + match c with + | 'A' + | 'C' + | 'G' + | 'T' -> + map + |> Map.change c (function + | Some v -> Some(v + 1) + | None -> Some 1) + | _ -> map + +let nucleotideCounts(strand: string) : Option> = + let map = Map [ 'A', 0; 'C', 0; 'G', 0; 'T', 0 ] + strand |> Seq.fold folder map |> Some diff --git a/exercises/practice/nucleotide-count/NucleotideCountTests.fs b/exercises/practice/nucleotide-count/NucleotideCountTests.fs index b006bbc1b..8373b6c4a 100644 --- a/exercises/practice/nucleotide-count/NucleotideCountTests.fs +++ b/exercises/practice/nucleotide-count/NucleotideCountTests.fs @@ -17,7 +17,7 @@ let ``Empty strand`` () = |> Some nucleotideCounts strand |> should equal expected -[] +[] let ``Can count one nucleotide in single-character input`` () = let strand = "G" let expected = @@ -29,7 +29,7 @@ let ``Can count one nucleotide in single-character input`` () = |> Some nucleotideCounts strand |> should equal expected -[] +[] let ``Strand with repeated nucleotide`` () = let strand = "GGGGGGG" let expected = @@ -41,7 +41,7 @@ let ``Strand with repeated nucleotide`` () = |> Some nucleotideCounts strand |> should equal expected -[] +[] let ``Strand with multiple nucleotides`` () = let strand = "AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC" let expected = @@ -53,7 +53,7 @@ let ``Strand with multiple nucleotides`` () = |> Some nucleotideCounts strand |> should equal expected -[] +[] let ``Strand with invalid nucleotides`` () = let strand = "AGXXACT" let expected = None From 9ab577dc627df873518dd484560b47945f356316 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Mon, 15 Jan 2024 08:50:35 +0100 Subject: [PATCH 19/39] Nucleotides: refacto --- .../nucleotide-count/NucleotideCount.fs | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/exercises/practice/nucleotide-count/NucleotideCount.fs b/exercises/practice/nucleotide-count/NucleotideCount.fs index a26f75c9a..017622686 100644 --- a/exercises/practice/nucleotide-count/NucleotideCount.fs +++ b/exercises/practice/nucleotide-count/NucleotideCount.fs @@ -1,17 +1,22 @@ module NucleotideCount +let nucleotides = [ 'A'; 'C'; 'G'; 'T' ] + +let isValid c = nucleotides |> List.contains c + let folder map (c: char) = - match c with - | 'A' - | 'C' - | 'G' - | 'T' -> + if (c |> isValid) then map |> Map.change c (function | Some v -> Some(v + 1) | None -> Some 1) - | _ -> map + else + map + let nucleotideCounts(strand: string) : Option> = - let map = Map [ 'A', 0; 'C', 0; 'G', 0; 'T', 0 ] - strand |> Seq.fold folder map |> Some + if strand |> Seq.exists (isValid >> not) then + None + else + let map = nucleotides |> List.map (fun c -> (c, 0)) |> Map.ofList + strand |> Seq.fold folder map |> Some From 113e6bba8d6335c33ed5a6d1b1171e808b383c53 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Mon, 15 Jan 2024 11:45:50 +0100 Subject: [PATCH 20/39] Ledger start refacto --- exercises/practice/ledger/Ledger.fs | 90 +++++++++++++++++++---------- 1 file changed, 60 insertions(+), 30 deletions(-) diff --git a/exercises/practice/ledger/Ledger.fs b/exercises/practice/ledger/Ledger.fs index 1871b59c7..d5bfdc50d 100644 --- a/exercises/practice/ledger/Ledger.fs +++ b/exercises/practice/ledger/Ledger.fs @@ -3,59 +3,89 @@ module Ledger open System open System.Globalization -type Entry = { dat: DateTime; des: string; chg: int } +type Locale = + | En + | Nl -let mkEntry (date: string) description change = { dat = DateTime.Parse(date, CultureInfo.InvariantCulture); des = description; chg = change } - -let formatLedger currency locale entries = - - let mutable res = "" +type Entry = + { dat: DateTime; des: string; chg: int } - if locale = "en-US" then res <- res + "Date | Description | Change " - if locale = "nl-NL" then res <- res + "Datum | Omschrijving | Verandering " - - for x in List.sortBy (fun x -> x.dat, x.des, x.chg) entries do +let mkEntry (date: string) description change = + { dat = DateTime.Parse(date, CultureInfo.InvariantCulture) + des = description + chg = change } + +let parseLocale = + function + | "en-US" -> En + | "nl-NL" -> Nl + | _ -> failwith "Damn it" - res <- res + "\n" +let title = + function + | En -> "Date | Description | Change " + | Nl -> "Datum | Omschrijving | Verandering " - if locale = "nl-NL" then +let processLine locale currency x = + let mutable res = "\n" + + if locale = "nl-NL" then res <- res + x.dat.ToString("dd-MM-yyyy") - if locale = "en-US" then + if locale = "en-US" then res <- res + x.dat.ToString("MM\/dd\/yyyy") - + res <- res + " | " - if x.des.Length <= 25 then - res <- res + x.des.PadRight(25) - elif x.des.Length = 25 then - res <- res + x.des - else - res <- res + x.des.[0..21] + "..." + if x.des.Length <= 25 then res <- res + x.des.PadRight(25) + elif x.des.Length = 25 then res <- res + x.des + else res <- res + x.des[0..21] + "..." res <- res + " | " let c = float x.chg / 100.0 - if c < 0.0 then + if c < 0.0 then if locale = "nl-NL" then if currency = "USD" then - res <- res + ("$ " + c.ToString("#,#0.00", new CultureInfo("nl-NL"))).PadLeft(13) + res <- res + ("$ " + c.ToString("#,#0.00", CultureInfo("nl-NL"))).PadLeft(13) + if currency = "EUR" then - res <- res + ("€ " + c.ToString("#,#0.00", new CultureInfo("nl-NL"))).PadLeft(13) + res <- res + ("€ " + c.ToString("#,#0.00", CultureInfo("nl-NL"))).PadLeft(13) + if locale = "en-US" then if currency = "USD" then - res <- res + ("($" + c.ToString("#,#0.00", new CultureInfo("en-US")).Substring(1) + ")").PadLeft(13) + res <- + res + + ("($" + c.ToString("#,#0.00", CultureInfo("en-US")).Substring(1) + ")") + .PadLeft(13) + if currency = "EUR" then - res <- res + ("(€" + c.ToString("#,#0.00", new CultureInfo("en-US")).Substring(1) + ")").PadLeft(13) - else + res <- + res + + ("(€" + c.ToString("#,#0.00", CultureInfo("en-US")).Substring(1) + ")") + .PadLeft(13) + else if locale = "nl-NL" then if currency = "USD" then - res <- res + ("$ " + c.ToString("#,#0.00", new CultureInfo("nl-NL")) + " ").PadLeft(13) + res <- res + ("$ " + c.ToString("#,#0.00", CultureInfo("nl-NL")) + " ").PadLeft(13) + if currency = "EUR" then - res <- res + ("€ " + c.ToString("#,#0.00", new CultureInfo("nl-NL")) + " ").PadLeft(13) + res <- res + ("€ " + c.ToString("#,#0.00", CultureInfo("nl-NL")) + " ").PadLeft(13) + if locale = "en-US" then if currency = "USD" then - res <- res + ("$" + c.ToString("#,#0.00", new CultureInfo("en-US")) + " ").PadLeft(13) + res <- res + ("$" + c.ToString("#,#0.00", CultureInfo("en-US")) + " ").PadLeft(13) + if currency = "EUR" then - res <- res + ("€" + c.ToString("#,#0.00", new CultureInfo("en-US")) + " ").PadLeft(13) + res <- res + ("€" + c.ToString("#,#0.00", CultureInfo("en-US")) + " ").PadLeft(13) + + res + +let formatLedger currency locale entries = + let l = locale |> parseLocale + let mutable res = title l + + for x in List.sortBy (fun x -> x.dat, x.des, x.chg) entries do + res <- res + (x|> processLine locale currency) + res From 1f6b783cf8c3886791fa1ef093ff184a47b71c48 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Mon, 15 Jan 2024 13:03:24 +0100 Subject: [PATCH 21/39] Replace currency and locale --- exercises/practice/ledger/Ledger.fs | 128 +++++++++++++++------------- 1 file changed, 70 insertions(+), 58 deletions(-) diff --git a/exercises/practice/ledger/Ledger.fs b/exercises/practice/ledger/Ledger.fs index d5bfdc50d..f6de27278 100644 --- a/exercises/practice/ledger/Ledger.fs +++ b/exercises/practice/ledger/Ledger.fs @@ -7,6 +7,10 @@ type Locale = | En | Nl +type Currency = + | EUR + | USD + type Entry = { dat: DateTime; des: string; chg: int } @@ -21,71 +25,79 @@ let parseLocale = | "nl-NL" -> Nl | _ -> failwith "Damn it" +let parseCurrency = + function + | "EUR" -> EUR + | "USD" -> USD + | _ -> failwith "Damn it" + let title = function | En -> "Date | Description | Change " | Nl -> "Datum | Omschrijving | Verandering " -let processLine locale currency x = - let mutable res = "\n" - - if locale = "nl-NL" then - res <- res + x.dat.ToString("dd-MM-yyyy") - - if locale = "en-US" then - res <- res + x.dat.ToString("MM\/dd\/yyyy") - - res <- res + " | " - - if x.des.Length <= 25 then res <- res + x.des.PadRight(25) - elif x.des.Length = 25 then res <- res + x.des - else res <- res + x.des[0..21] + "..." - - res <- res + " | " - let c = float x.chg / 100.0 - - if c < 0.0 then - if locale = "nl-NL" then - if currency = "USD" then - res <- res + ("$ " + c.ToString("#,#0.00", CultureInfo("nl-NL"))).PadLeft(13) - - if currency = "EUR" then - res <- res + ("€ " + c.ToString("#,#0.00", CultureInfo("nl-NL"))).PadLeft(13) - - if locale = "en-US" then - if currency = "USD" then - res <- - res - + ("($" + c.ToString("#,#0.00", CultureInfo("en-US")).Substring(1) + ")") - .PadLeft(13) - - if currency = "EUR" then - res <- - res - + ("(€" + c.ToString("#,#0.00", CultureInfo("en-US")).Substring(1) + ")") - .PadLeft(13) - else - if locale = "nl-NL" then - if currency = "USD" then - res <- res + ("$ " + c.ToString("#,#0.00", CultureInfo("nl-NL")) + " ").PadLeft(13) - - if currency = "EUR" then - res <- res + ("€ " + c.ToString("#,#0.00", CultureInfo("nl-NL")) + " ").PadLeft(13) - - if locale = "en-US" then - if currency = "USD" then - res <- res + ("$" + c.ToString("#,#0.00", CultureInfo("en-US")) + " ").PadLeft(13) - - if currency = "EUR" then - res <- res + ("€" + c.ToString("#,#0.00", CultureInfo("en-US")) + " ").PadLeft(13) - - res - +let processLine locale (currency: Currency) line = + let mutable res = "\n" + + match locale with + | Nl -> res <- res + line.dat.ToString("dd-MM-yyyy") + | En -> res <- res + line.dat.ToString("MM\/dd\/yyyy") + + res <- res + " | " + + if line.des.Length <= 25 then + res <- res + line.des.PadRight(25) + elif line.des.Length = 25 then + res <- res + line.des + else + res <- res + line.des[0..21] + "..." + + res <- res + " | " + let c = float line.chg / 100.0 + + if c < 0.0 then + match locale with + | Nl -> + match currency with + | USD -> res <- res + ("$ " + c.ToString("#,#0.00", CultureInfo("nl-NL"))).PadLeft(13) + + | EUR -> res <- res + ("€ " + c.ToString("#,#0.00", CultureInfo("nl-NL"))).PadLeft(13) + + | En -> + match currency with + | USD -> + res <- + res + + ("($" + c.ToString("#,#0.00", CultureInfo("en-US")).Substring(1) + ")") + .PadLeft(13) + + | EUR -> + res <- + res + + ("(€" + c.ToString("#,#0.00", CultureInfo("en-US")).Substring(1) + ")") + .PadLeft(13) + else + match locale with + | Nl -> + match currency with + | USD -> res <- res + ("$ " + c.ToString("#,#0.00", CultureInfo("nl-NL")) + " ").PadLeft(13) + + | EUR -> res <- res + ("€ " + c.ToString("#,#0.00", CultureInfo("nl-NL")) + " ").PadLeft(13) + + | En -> + match currency with + | USD -> res <- res + ("$" + c.ToString("#,#0.00", CultureInfo("en-US")) + " ").PadLeft(13) + + | EUR -> res <- res + ("€" + c.ToString("#,#0.00", CultureInfo("en-US")) + " ").PadLeft(13) + + res + let formatLedger currency locale entries = - let l = locale |> parseLocale - let mutable res = title l + let locale = locale |> parseLocale + let currency = currency |> parseCurrency + let mutable res = title locale for x in List.sortBy (fun x -> x.dat, x.des, x.chg) entries do - res <- res + (x|> processLine locale currency) + res <- res + (x |> processLine locale currency) res From 7ec19a21596d047400026bc405db42b9ba02db9b Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Mon, 15 Jan 2024 13:13:02 +0100 Subject: [PATCH 22/39] Use fold --- exercises/practice/ledger/Ledger.fs | 39 ++++++++++++++--------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/exercises/practice/ledger/Ledger.fs b/exercises/practice/ledger/Ledger.fs index f6de27278..f6aeef769 100644 --- a/exercises/practice/ledger/Ledger.fs +++ b/exercises/practice/ledger/Ledger.fs @@ -12,12 +12,12 @@ type Currency = | USD type Entry = - { dat: DateTime; des: string; chg: int } + { Date: DateTime; Description: string; Change: int } let mkEntry (date: string) description change = - { dat = DateTime.Parse(date, CultureInfo.InvariantCulture) - des = description - chg = change } + { Date = DateTime.Parse(date, CultureInfo.InvariantCulture) + Description = description + Change = change } let parseLocale = function @@ -31,29 +31,29 @@ let parseCurrency = | "USD" -> USD | _ -> failwith "Damn it" -let title = +let getTitle = function | En -> "Date | Description | Change " | Nl -> "Datum | Omschrijving | Verandering " -let processLine locale (currency: Currency) line = +let processLine locale currency state line = let mutable res = "\n" match locale with - | Nl -> res <- res + line.dat.ToString("dd-MM-yyyy") - | En -> res <- res + line.dat.ToString("MM\/dd\/yyyy") + | Nl -> res <- res + line.Date.ToString("dd-MM-yyyy") + | En -> res <- res + line.Date.ToString("MM\/dd\/yyyy") res <- res + " | " - if line.des.Length <= 25 then - res <- res + line.des.PadRight(25) - elif line.des.Length = 25 then - res <- res + line.des + if line.Description.Length <= 25 then + res <- res + line.Description.PadRight(25) + elif line.Description.Length = 25 then + res <- res + line.Description else - res <- res + line.des[0..21] + "..." + res <- res + line.Description[0..21] + "..." res <- res + " | " - let c = float line.chg / 100.0 + let c = float line.Change / 100.0 if c < 0.0 then match locale with @@ -90,14 +90,13 @@ let processLine locale (currency: Currency) line = | EUR -> res <- res + ("€" + c.ToString("#,#0.00", CultureInfo("en-US")) + " ").PadLeft(13) - res + state + res let formatLedger currency locale entries = let locale = locale |> parseLocale let currency = currency |> parseCurrency - let mutable res = title locale + let folder = processLine locale currency - for x in List.sortBy (fun x -> x.dat, x.des, x.chg) entries do - res <- res + (x |> processLine locale currency) - - res + entries + |> List.sortBy (fun entry -> entry.Date, entry.Description, entry.Change) + |> List.fold folder (getTitle locale) From 4277c2f732b6cf92fbc39b7219c716915b6131ec Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Mon, 15 Jan 2024 13:26:58 +0100 Subject: [PATCH 23/39] Refacto --- exercises/practice/ledger/Ledger.fs | 71 +++++++++++++++-------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/exercises/practice/ledger/Ledger.fs b/exercises/practice/ledger/Ledger.fs index f6aeef769..ec9539f57 100644 --- a/exercises/practice/ledger/Ledger.fs +++ b/exercises/practice/ledger/Ledger.fs @@ -12,7 +12,9 @@ type Currency = | USD type Entry = - { Date: DateTime; Description: string; Change: int } + { Date: DateTime + Description: string + Change: int } let mkEntry (date: string) description change = { Date = DateTime.Parse(date, CultureInfo.InvariantCulture) @@ -36,67 +38,66 @@ let getTitle = | En -> "Date | Description | Change " | Nl -> "Datum | Omschrijving | Verandering " -let processLine locale currency state line = - let mutable res = "\n" - +let getDate locale entry = match locale with - | Nl -> res <- res + line.Date.ToString("dd-MM-yyyy") - | En -> res <- res + line.Date.ToString("MM\/dd\/yyyy") + | Nl -> entry.Date.ToString("dd-MM-yyyy") + | En -> entry.Date.ToString("MM\/dd\/yyyy") + +let getCurrencySign = + function + | EUR -> "€" + | USD -> "$ " - res <- res + " | " +let getCulture = + function + | Nl -> "nl-NL" + | En -> "en-US" - if line.Description.Length <= 25 then - res <- res + line.Description.PadRight(25) - elif line.Description.Length = 25 then - res <- res + line.Description +let getDescription entry = + if entry.Description.Length <= 25 then + entry.Description.PadRight(25) else - res <- res + line.Description[0..21] + "..." + entry.Description[0..21] + "..." - res <- res + " | " - let c = float line.Change / 100.0 +let getAmount locale currency entry = + let amount = float entry.Change / 100.0 - if c < 0.0 then + if amount < 0.0 then match locale with | Nl -> match currency with - | USD -> res <- res + ("$ " + c.ToString("#,#0.00", CultureInfo("nl-NL"))).PadLeft(13) - - | EUR -> res <- res + ("€ " + c.ToString("#,#0.00", CultureInfo("nl-NL"))).PadLeft(13) + | USD -> ("$ " + amount.ToString("#,#0.00", CultureInfo("nl-NL"))).PadLeft(13) + | EUR -> ("€ " + amount.ToString("#,#0.00", CultureInfo("nl-NL"))).PadLeft(13) | En -> match currency with | USD -> - res <- - res - + ("($" + c.ToString("#,#0.00", CultureInfo("en-US")).Substring(1) + ")") - .PadLeft(13) + ("($" + amount.ToString("#,#0.00", CultureInfo("en-US")).Substring(1) + ")") + .PadLeft(13) | EUR -> - res <- - res - + ("(€" + c.ToString("#,#0.00", CultureInfo("en-US")).Substring(1) + ")") - .PadLeft(13) + ("(€" + amount.ToString("#,#0.00", CultureInfo("en-US")).Substring(1) + ")") + .PadLeft(13) else match locale with | Nl -> match currency with - | USD -> res <- res + ("$ " + c.ToString("#,#0.00", CultureInfo("nl-NL")) + " ").PadLeft(13) - - | EUR -> res <- res + ("€ " + c.ToString("#,#0.00", CultureInfo("nl-NL")) + " ").PadLeft(13) + | USD -> ("$ " + amount.ToString("#,#0.00", CultureInfo("nl-NL")) + " ").PadLeft(13) + | EUR -> ("€ " + amount.ToString("#,#0.00", CultureInfo("nl-NL")) + " ").PadLeft(13) | En -> match currency with - | USD -> res <- res + ("$" + c.ToString("#,#0.00", CultureInfo("en-US")) + " ").PadLeft(13) - - | EUR -> res <- res + ("€" + c.ToString("#,#0.00", CultureInfo("en-US")) + " ").PadLeft(13) + | USD -> ("$" + amount.ToString("#,#0.00", CultureInfo("en-US")) + " ").PadLeft(13) + | EUR -> ("€" + amount.ToString("#,#0.00", CultureInfo("en-US")) + " ").PadLeft(13) - state + res +let generateLine locale currency entry = + $"\n{(entry |> getDate locale)} | {(entry |> getDescription)} | {(entry |> getAmount locale currency)}" let formatLedger currency locale entries = let locale = locale |> parseLocale let currency = currency |> parseCurrency - let folder = processLine locale currency + let generate = generateLine locale currency entries |> List.sortBy (fun entry -> entry.Date, entry.Description, entry.Change) - |> List.fold folder (getTitle locale) + |> List.fold (fun state entry -> state + (generate entry)) (getTitle locale) From 37a847a43b034110573e467d649e2c4a3351fc4d Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Tue, 16 Jan 2024 10:49:58 +0100 Subject: [PATCH 24/39] Refacto getAmount --- exercises/practice/ledger/Ledger.fs | 58 ++++++++++++----------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/exercises/practice/ledger/Ledger.fs b/exercises/practice/ledger/Ledger.fs index ec9539f57..f4b3aefcb 100644 --- a/exercises/practice/ledger/Ledger.fs +++ b/exercises/practice/ledger/Ledger.fs @@ -39,19 +39,22 @@ let getTitle = | Nl -> "Datum | Omschrijving | Verandering " let getDate locale entry = - match locale with - | Nl -> entry.Date.ToString("dd-MM-yyyy") - | En -> entry.Date.ToString("MM\/dd\/yyyy") + let format = + match locale with + | Nl -> "dd-MM-yyyy" + | En -> "MM\/dd\/yyyy" + + entry.Date.ToString(format) let getCurrencySign = function | EUR -> "€" - | USD -> "$ " + | USD -> "$" -let getCulture = +let getCultureInfo = function - | Nl -> "nl-NL" - | En -> "en-US" + | Nl -> CultureInfo("nl-NL") + | En -> CultureInfo("en-US") let getDescription entry = if entry.Description.Length <= 25 then @@ -62,33 +65,20 @@ let getDescription entry = let getAmount locale currency entry = let amount = float entry.Change / 100.0 - if amount < 0.0 then - match locale with - | Nl -> - match currency with - | USD -> ("$ " + amount.ToString("#,#0.00", CultureInfo("nl-NL"))).PadLeft(13) - | EUR -> ("€ " + amount.ToString("#,#0.00", CultureInfo("nl-NL"))).PadLeft(13) - - | En -> - match currency with - | USD -> - ("($" + amount.ToString("#,#0.00", CultureInfo("en-US")).Substring(1) + ")") - .PadLeft(13) - - | EUR -> - ("(€" + amount.ToString("#,#0.00", CultureInfo("en-US")).Substring(1) + ")") - .PadLeft(13) - else - match locale with - | Nl -> - match currency with - | USD -> ("$ " + amount.ToString("#,#0.00", CultureInfo("nl-NL")) + " ").PadLeft(13) - | EUR -> ("€ " + amount.ToString("#,#0.00", CultureInfo("nl-NL")) + " ").PadLeft(13) - - | En -> - match currency with - | USD -> ("$" + amount.ToString("#,#0.00", CultureInfo("en-US")) + " ").PadLeft(13) - | EUR -> ("€" + amount.ToString("#,#0.00", CultureInfo("en-US")) + " ").PadLeft(13) + let sign = currency |> getCurrencySign + let amountTxt = amount.ToString("#,#0.00", locale |> getCultureInfo) + + let line = + if amount < 0.0 then + match locale with + | Nl -> $"{sign} {amountTxt}" + | En -> $"({sign}{amountTxt.Substring(1)})" + else + match locale with + | Nl -> $"{sign} {amountTxt} " + | En -> $"{sign}{amountTxt} " + + line.PadLeft(13) let generateLine locale currency entry = $"\n{(entry |> getDate locale)} | {(entry |> getDescription)} | {(entry |> getAmount locale currency)}" From 0594ee4c39148ff4728dc7726dfc38227736f074 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Wed, 7 Feb 2024 09:52:46 +0100 Subject: [PATCH 25/39] Solve acronym --- exercises/practice/acronym/Acronym.fs | 10 +++++++++- exercises/practice/acronym/AcronymTests.fs | 17 ++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/exercises/practice/acronym/Acronym.fs b/exercises/practice/acronym/Acronym.fs index 2d89ecbf2..b6a032a05 100644 --- a/exercises/practice/acronym/Acronym.fs +++ b/exercises/practice/acronym/Acronym.fs @@ -1,3 +1,11 @@ module Acronym -let abbreviate phrase = failwith "You need to implement this function." \ No newline at end of file +open System + +let abbreviate(phrase: string) = + phrase + .Replace('-', ' ') + .Replace('_', ' ') + .Split(' ', StringSplitOptions.RemoveEmptyEntries) + |> Array.map (fun s -> s[0] |> Char.ToUpper |> string) + |> String.concat "" diff --git a/exercises/practice/acronym/AcronymTests.fs b/exercises/practice/acronym/AcronymTests.fs index e225108b1..2ad5b7b17 100644 --- a/exercises/practice/acronym/AcronymTests.fs +++ b/exercises/practice/acronym/AcronymTests.fs @@ -9,35 +9,34 @@ open Acronym let ``Basic`` () = abbreviate "Portable Network Graphics" |> should equal "PNG" -[] +[] let ``Lowercase words`` () = abbreviate "Ruby on Rails" |> should equal "ROR" -[] +[] let ``Punctuation`` () = abbreviate "First In, First Out" |> should equal "FIFO" -[] +[] let ``All caps word`` () = abbreviate "GNU Image Manipulation Program" |> should equal "GIMP" -[] +[] let ``Punctuation without whitespace`` () = abbreviate "Complementary metal-oxide semiconductor" |> should equal "CMOS" -[] +[] let ``Very long abbreviation`` () = abbreviate "Rolling On The Floor Laughing So Hard That My Dogs Came Over And Licked Me" |> should equal "ROTFLSHTMDCOALM" -[] +[] let ``Consecutive delimiters`` () = abbreviate "Something - I made up from thin air" |> should equal "SIMUFTA" -[] +[] let ``Apostrophes`` () = abbreviate "Halley's Comet" |> should equal "HC" -[] +[] let ``Underscore emphasis`` () = abbreviate "The Road _Not_ Taken" |> should equal "TRNT" - From 25ef9a9981511315f52251a1b06728be063c0454 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Wed, 7 Feb 2024 12:53:02 +0100 Subject: [PATCH 26/39] Refacto Acronym Solve Anagram --- exercises/practice/acronym/Acronym.fs | 9 +++---- exercises/practice/anagram/Anagram.fs | 18 ++++++++++++- exercises/practice/anagram/AnagramTests.fs | 31 +++++++++++----------- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/exercises/practice/acronym/Acronym.fs b/exercises/practice/acronym/Acronym.fs index b6a032a05..a70963830 100644 --- a/exercises/practice/acronym/Acronym.fs +++ b/exercises/practice/acronym/Acronym.fs @@ -4,8 +4,7 @@ open System let abbreviate(phrase: string) = phrase - .Replace('-', ' ') - .Replace('_', ' ') - .Split(' ', StringSplitOptions.RemoveEmptyEntries) - |> Array.map (fun s -> s[0] |> Char.ToUpper |> string) - |> String.concat "" + .ToUpper() + .Split([| ' '; '-'; '_' |], StringSplitOptions.RemoveEmptyEntries) + |> Array.map (fun s -> s[0]) + |> String.Concat diff --git a/exercises/practice/anagram/Anagram.fs b/exercises/practice/anagram/Anagram.fs index 231d6839e..4bb864215 100644 --- a/exercises/practice/anagram/Anagram.fs +++ b/exercises/practice/anagram/Anagram.fs @@ -1,3 +1,19 @@ module Anagram -let findAnagrams sources target = failwith "You need to implement this function." \ No newline at end of file +open System + +let isAnagram (target: string) (source: string) = + if target.Equals(source, StringComparison.OrdinalIgnoreCase) then + false + else + let sort(str: string) = + str.ToLower() + |> Seq.sort + |> Seq.toArray + |> (function + | chars -> String.Concat chars) + + (target |> sort) = (source |> sort) + +let findAnagrams sources target = + sources |> List.filter (isAnagram target) diff --git a/exercises/practice/anagram/AnagramTests.fs b/exercises/practice/anagram/AnagramTests.fs index 41c7939c9..84ca124dc 100644 --- a/exercises/practice/anagram/AnagramTests.fs +++ b/exercises/practice/anagram/AnagramTests.fs @@ -10,78 +10,77 @@ let ``No matches`` () = let candidates = ["hello"; "world"; "zombies"; "pants"] findAnagrams candidates "diaper" |> should be Empty -[] +[] let ``Detects two anagrams`` () = let candidates = ["lemons"; "cherry"; "melons"] findAnagrams candidates "solemn" |> should equal ["lemons"; "melons"] -[] +[] let ``Does not detect anagram subsets`` () = let candidates = ["dog"; "goody"] findAnagrams candidates "good" |> should be Empty -[] +[] let ``Detects anagram`` () = let candidates = ["enlists"; "google"; "inlets"; "banana"] findAnagrams candidates "listen" |> should equal ["inlets"] -[] +[] let ``Detects three anagrams`` () = let candidates = ["gallery"; "ballerina"; "regally"; "clergy"; "largely"; "leading"] findAnagrams candidates "allergy" |> should equal ["gallery"; "regally"; "largely"] -[] +[] let ``Detects multiple anagrams with different case`` () = let candidates = ["Eons"; "ONES"] findAnagrams candidates "nose" |> should equal ["Eons"; "ONES"] -[] +[] let ``Does not detect non-anagrams with identical checksum`` () = let candidates = ["last"] findAnagrams candidates "mass" |> should be Empty -[] +[] let ``Detects anagrams case-insensitively`` () = let candidates = ["cashregister"; "Carthorse"; "radishes"] findAnagrams candidates "Orchestra" |> should equal ["Carthorse"] -[] +[] let ``Detects anagrams using case-insensitive subject`` () = let candidates = ["cashregister"; "carthorse"; "radishes"] findAnagrams candidates "Orchestra" |> should equal ["carthorse"] -[] +[] let ``Detects anagrams using case-insensitive possible matches`` () = let candidates = ["cashregister"; "Carthorse"; "radishes"] findAnagrams candidates "orchestra" |> should equal ["Carthorse"] -[] +[] let ``Does not detect an anagram if the original word is repeated`` () = let candidates = ["go Go GO"] findAnagrams candidates "go" |> should be Empty -[] +[] let ``Anagrams must use all letters exactly once`` () = let candidates = ["patter"] findAnagrams candidates "tapper" |> should be Empty -[] +[] let ``Words are not anagrams of themselves`` () = let candidates = ["BANANA"] findAnagrams candidates "BANANA" |> should be Empty -[] +[] let ``Words are not anagrams of themselves even if letter case is partially different`` () = let candidates = ["Banana"] findAnagrams candidates "BANANA" |> should be Empty -[] +[] let ``Words are not anagrams of themselves even if letter case is completely different`` () = let candidates = ["banana"] findAnagrams candidates "BANANA" |> should be Empty -[] +[] let ``Words other than themselves can be anagrams`` () = let candidates = ["LISTEN"; "Silent"] findAnagrams candidates "LISTEN" |> should equal ["Silent"] - From b037a587c270dfcd08f29f1320382a8fd740223c Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Thu, 8 Feb 2024 08:51:26 +0100 Subject: [PATCH 27/39] Simplify Anagram using LINQ --- exercises/practice/anagram/Anagram.fs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/exercises/practice/anagram/Anagram.fs b/exercises/practice/anagram/Anagram.fs index 4bb864215..39ba0a37a 100644 --- a/exercises/practice/anagram/Anagram.fs +++ b/exercises/practice/anagram/Anagram.fs @@ -1,19 +1,15 @@ module Anagram open System +open System.Linq let isAnagram (target: string) (source: string) = if target.Equals(source, StringComparison.OrdinalIgnoreCase) then false else - let sort(str: string) = - str.ToLower() - |> Seq.sort - |> Seq.toArray - |> (function - | chars -> String.Concat chars) + let sort(str: string) = str.ToLower() |> Seq.sort - (target |> sort) = (source |> sort) + (target |> sort).SequenceEqual(source |> sort) let findAnagrams sources target = sources |> List.filter (isAnagram target) From 740a9029ffa2ec87802cbd01d92bddbb555e50f2 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Thu, 8 Feb 2024 08:53:49 +0100 Subject: [PATCH 28/39] Refacto Ledger --- exercises/practice/ledger/Ledger.fs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/exercises/practice/ledger/Ledger.fs b/exercises/practice/ledger/Ledger.fs index f4b3aefcb..f3cd2f62a 100644 --- a/exercises/practice/ledger/Ledger.fs +++ b/exercises/practice/ledger/Ledger.fs @@ -80,14 +80,13 @@ let getAmount locale currency entry = line.PadLeft(13) -let generateLine locale currency entry = - $"\n{(entry |> getDate locale)} | {(entry |> getDescription)} | {(entry |> getAmount locale currency)}" - let formatLedger currency locale entries = let locale = locale |> parseLocale let currency = currency |> parseCurrency - let generate = generateLine locale currency + + let generate entry = + $"\n{(entry |> getDate locale)} | {(entry |> getDescription)} | {(entry |> getAmount locale currency)}" entries |> List.sortBy (fun entry -> entry.Date, entry.Description, entry.Change) - |> List.fold (fun state entry -> state + (generate entry)) (getTitle locale) + |> List.fold (fun state entry -> state + (entry |> generate)) (getTitle locale) From 5f641521a647c07b845da29a3772c0ff64a9be1d Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Tue, 20 Feb 2024 14:06:00 +0100 Subject: [PATCH 29/39] Start bookstore --- exercises/practice/book-store/BookStore.fs | 20 ++++++++++++++++++- .../practice/book-store/BookStoreTests.fs | 17 ++++++++-------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/exercises/practice/book-store/BookStore.fs b/exercises/practice/book-store/BookStore.fs index 27279224f..72efa00a6 100644 --- a/exercises/practice/book-store/BookStore.fs +++ b/exercises/practice/book-store/BookStore.fs @@ -1,3 +1,21 @@ module BookStore -let total books = failwith "You need to implement this function." \ No newline at end of file +let getDiscount = + function + | 2 -> 0.95m + | 3 -> 0.90m + | 4 -> 0.8m + | 5 -> 0.75m + | _ -> 1m + +let computeForFiveOrLess books = + let distinct = List.distinct books |> List.length + (books |> List.length |> decimal) * 8m * getDiscount distinct + +let computePrice books = + 0m + +let total books = + match List.length books with + | n when n <= 5 -> computeForFiveOrLess books + | _ -> computePrice books diff --git a/exercises/practice/book-store/BookStoreTests.fs b/exercises/practice/book-store/BookStoreTests.fs index 1a66ebb3f..db6167daf 100644 --- a/exercises/practice/book-store/BookStoreTests.fs +++ b/exercises/practice/book-store/BookStoreTests.fs @@ -9,31 +9,31 @@ open BookStore let ``Only a single book`` () = total [1] |> should equal 8.00m -[] +[] let ``Two of the same book`` () = total [2; 2] |> should equal 16.00m -[] +[] let ``Empty basket`` () = total [] |> should equal 0.00m -[] +[] let ``Two different books`` () = total [1; 2] |> should equal 15.20m -[] +[] let ``Three different books`` () = total [1; 2; 3] |> should equal 21.60m -[] +[] let ``Four different books`` () = total [1; 2; 3; 4] |> should equal 25.60m -[] +[] let ``Five different books`` () = total [1; 2; 3; 4; 5] |> should equal 30.00m -[] +[] let ``Two groups of four is cheaper than group of five plus group of three`` () = total [1; 1; 2; 2; 3; 3; 4; 5] |> should equal 51.20m @@ -49,7 +49,7 @@ let ``Group of four plus group of two is cheaper than two groups of three`` () = let ``Two each of first four books and one copy each of rest`` () = total [1; 1; 2; 2; 3; 3; 4; 4; 5] |> should equal 55.60m -[] +[] let ``Two copies of each book`` () = total [1; 1; 2; 2; 3; 3; 4; 4; 5; 5] |> should equal 60.00m @@ -76,4 +76,3 @@ let ``One group of one and four is cheaper than one group of two and three`` () [] let ``One group of one and two plus three groups of four is cheaper than one group of each size`` () = total [1; 2; 2; 3; 3; 3; 4; 4; 4; 4; 5; 5; 5; 5; 5] |> should equal 100.00m - From c2b3475c3e31f568487e2875554559ad8f0d36a0 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Tue, 20 Feb 2024 15:33:22 +0100 Subject: [PATCH 30/39] Solve Clock.fs --- exercises/practice/clock/Clock.fs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/exercises/practice/clock/Clock.fs b/exercises/practice/clock/Clock.fs index 52cf64e4b..61348ad04 100644 --- a/exercises/practice/clock/Clock.fs +++ b/exercises/practice/clock/Clock.fs @@ -1,18 +1,24 @@ module Clock -type Clock = { - Hours: int - Minutes: int -} -let create hours minutes = +type Clock = { Hours: int; Minutes: int } + +let createFromMinutes totalMinutes = + let minutes = totalMinutes % 1440 + let minutes = if (minutes < 0) then 1440 + minutes else minutes + let m = minutes % 60 let h = minutes / 60 - let hours = if (hours < 0) then 24 + (hours % 24) else (hours % 24) + h - { Hours = hours; Minutes = m } + { Hours = h; Minutes = m } + +let create hours minutes = + createFromMinutes (hours * 60 + minutes) -let add minutes clock = create clock.Hours (clock.Minutes + minutes) +let add minutes clock = + createFromMinutes (clock.Hours * 60 + clock.Minutes + minutes) -let subtract minutes clock = create clock.Hours (clock.Minutes - minutes) +let subtract minutes clock = + createFromMinutes (clock.Hours * 60 + clock.Minutes - minutes) -let display clock = sprintf "%s:%s" (clock.Hours.ToString("00")) (clock.Minutes.ToString("00")) \ No newline at end of file +let display clock = + sprintf "%s:%s" (clock.Hours.ToString("00")) (clock.Minutes.ToString("00")) From c90d35e403b4c190d3d5f242df3f753cc8ea1fad Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Wed, 21 Feb 2024 09:16:20 +0100 Subject: [PATCH 31/39] Solve RotationalCipher --- .../rotational-cipher/RotationalCipher.fs | 25 ++++++++++++++++++- .../RotationalCipherTests.fs | 19 +++++++------- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/exercises/practice/rotational-cipher/RotationalCipher.fs b/exercises/practice/rotational-cipher/RotationalCipher.fs index cd31332f1..304990ba7 100644 --- a/exercises/practice/rotational-cipher/RotationalCipher.fs +++ b/exercises/practice/rotational-cipher/RotationalCipher.fs @@ -1,3 +1,26 @@ module RotationalCipher -let rotate shiftKey text = failwith "You need to implement this function." \ No newline at end of file +open System + +let (|Upper|Lower|Other|) = + function + | c when c |> Char.IsUpper -> Upper + | c when c |> Char.IsLower -> Lower + | _ -> Other + +let encrypt shiftKey = + let uppers = [ 'A' .. 'Z' ] + let lowers = [ 'a' .. 'z' ] + + fun char -> + let rotate chars = + let idx = chars |> List.findIndex ((=) char) + chars[(idx + shiftKey) % 26] + + match char with + | Upper -> uppers |> rotate + | Lower -> lowers |> rotate + | Other -> char + +let rotate (shiftKey: int) (text: string) = + text |> Seq.map (encrypt shiftKey) |> String.Concat diff --git a/exercises/practice/rotational-cipher/RotationalCipherTests.fs b/exercises/practice/rotational-cipher/RotationalCipherTests.fs index ca3491298..fe054000b 100644 --- a/exercises/practice/rotational-cipher/RotationalCipherTests.fs +++ b/exercises/practice/rotational-cipher/RotationalCipherTests.fs @@ -9,39 +9,38 @@ open RotationalCipher let ``Rotate a by 0, same output as input`` () = rotate 0 "a" |> should equal "a" -[] +[] let ``Rotate a by 1`` () = rotate 1 "a" |> should equal "b" -[] +[] let ``Rotate a by 26, same output as input`` () = rotate 26 "a" |> should equal "a" -[] +[] let ``Rotate m by 13`` () = rotate 13 "m" |> should equal "z" -[] +[] let ``Rotate n by 13 with wrap around alphabet`` () = rotate 13 "n" |> should equal "a" -[] +[] let ``Rotate capital letters`` () = rotate 5 "OMG" |> should equal "TRL" -[] +[] let ``Rotate spaces`` () = rotate 5 "O M G" |> should equal "T R L" -[] +[] let ``Rotate numbers`` () = rotate 4 "Testing 1 2 3 testing" |> should equal "Xiwxmrk 1 2 3 xiwxmrk" -[] +[] let ``Rotate punctuation`` () = rotate 21 "Let's eat, Grandma!" |> should equal "Gzo'n zvo, Bmviyhv!" -[] +[] let ``Rotate all letters`` () = rotate 13 "The quick brown fox jumps over the lazy dog." |> should equal "Gur dhvpx oebja sbk whzcf bire gur ynml qbt." - From a7214bc2f495960e1b2c5d06aa0e1ae6b94b4d4c Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Wed, 21 Feb 2024 11:30:51 +0100 Subject: [PATCH 32/39] Meetup : ugly solve --- exercises/practice/meetup/Meetup.fs | 62 +++++++- exercises/practice/meetup/MeetupTests.fs | 189 +++++++++++------------ 2 files changed, 154 insertions(+), 97 deletions(-) diff --git a/exercises/practice/meetup/Meetup.fs b/exercises/practice/meetup/Meetup.fs index 841143684..f166a31de 100644 --- a/exercises/practice/meetup/Meetup.fs +++ b/exercises/practice/meetup/Meetup.fs @@ -2,6 +2,64 @@ module Meetup open System -type Week = First | Second | Third | Fourth | Last | Teenth +type Week = + | First + | Second + | Third + | Fourth + | Last + | Teenth -let meetup year month week dayOfWeek: DateTime = failwith "You need to implement this function." \ No newline at end of file +let isTeenth = + function + | 13 + | 14 + | 15 + | 16 + | 17 + | 18 + | 19 -> true + | _ -> false + +let countOk week count = + match week with + | First -> count = 1 + | Second -> count = 2 + | Third -> count = 3 + | Fourth -> count = 4 + | _ -> false + +let rec findDate (date: DateTime) week dayOfWeek count = + if date.DayOfWeek = dayOfWeek then + match week with + | Teenth -> + if (date.Day |> isTeenth) then + date + else + (findDate (date.AddDays(1)) week dayOfWeek count) + | First + | Second + | Third + | Fourth -> + if countOk week count then + date + else + (findDate (date.AddDays(1)) week dayOfWeek (count + 1)) + + | Last -> date + else + findDate (date.AddDays(1)) week dayOfWeek count + +let rec findDateRev (date: DateTime) dayOfWeek = + if (date.DayOfWeek = dayOfWeek) then + date + else + findDateRev (date.AddDays(-1)) dayOfWeek + +let meetup year month week dayOfWeek : DateTime = + let date = DateTime(year, month, 1) + + if (week = Last) then + findDateRev (date.AddMonths(1).AddDays(-1)) dayOfWeek + else + findDate date week dayOfWeek 1 diff --git a/exercises/practice/meetup/MeetupTests.fs b/exercises/practice/meetup/MeetupTests.fs index 1472fb9ce..d3ea699ef 100644 --- a/exercises/practice/meetup/MeetupTests.fs +++ b/exercises/practice/meetup/MeetupTests.fs @@ -10,379 +10,378 @@ open Meetup let ``When teenth Monday is the 13th, the first day of the teenth week`` () = meetup 2013 5 Week.Teenth DayOfWeek.Monday |> should equal (DateTime(2013, 5, 13)) -[] +[] let ``When teenth Monday is the 19th, the last day of the teenth week`` () = meetup 2013 8 Week.Teenth DayOfWeek.Monday |> should equal (DateTime(2013, 8, 19)) -[] +[] let ``When teenth Monday is some day in the middle of the teenth week`` () = meetup 2013 9 Week.Teenth DayOfWeek.Monday |> should equal (DateTime(2013, 9, 16)) -[] +[] let ``When teenth Tuesday is the 19th, the last day of the teenth week`` () = meetup 2013 3 Week.Teenth DayOfWeek.Tuesday |> should equal (DateTime(2013, 3, 19)) -[] +[] let ``When teenth Tuesday is some day in the middle of the teenth week`` () = meetup 2013 4 Week.Teenth DayOfWeek.Tuesday |> should equal (DateTime(2013, 4, 16)) -[] +[] let ``When teenth Tuesday is the 13th, the first day of the teenth week`` () = meetup 2013 8 Week.Teenth DayOfWeek.Tuesday |> should equal (DateTime(2013, 8, 13)) -[] +[] let ``When teenth Wednesday is some day in the middle of the teenth week`` () = meetup 2013 1 Week.Teenth DayOfWeek.Wednesday |> should equal (DateTime(2013, 1, 16)) -[] +[] let ``When teenth Wednesday is the 13th, the first day of the teenth week`` () = meetup 2013 2 Week.Teenth DayOfWeek.Wednesday |> should equal (DateTime(2013, 2, 13)) -[] +[] let ``When teenth Wednesday is the 19th, the last day of the teenth week`` () = meetup 2013 6 Week.Teenth DayOfWeek.Wednesday |> should equal (DateTime(2013, 6, 19)) -[] +[] let ``When teenth Thursday is some day in the middle of the teenth week`` () = meetup 2013 5 Week.Teenth DayOfWeek.Thursday |> should equal (DateTime(2013, 5, 16)) -[] +[] let ``When teenth Thursday is the 13th, the first day of the teenth week`` () = meetup 2013 6 Week.Teenth DayOfWeek.Thursday |> should equal (DateTime(2013, 6, 13)) -[] +[] let ``When teenth Thursday is the 19th, the last day of the teenth week`` () = meetup 2013 9 Week.Teenth DayOfWeek.Thursday |> should equal (DateTime(2013, 9, 19)) -[] +[] let ``When teenth Friday is the 19th, the last day of the teenth week`` () = meetup 2013 4 Week.Teenth DayOfWeek.Friday |> should equal (DateTime(2013, 4, 19)) -[] +[] let ``When teenth Friday is some day in the middle of the teenth week`` () = meetup 2013 8 Week.Teenth DayOfWeek.Friday |> should equal (DateTime(2013, 8, 16)) -[] +[] let ``When teenth Friday is the 13th, the first day of the teenth week`` () = meetup 2013 9 Week.Teenth DayOfWeek.Friday |> should equal (DateTime(2013, 9, 13)) -[] +[] let ``When teenth Saturday is some day in the middle of the teenth week`` () = meetup 2013 2 Week.Teenth DayOfWeek.Saturday |> should equal (DateTime(2013, 2, 16)) -[] +[] let ``When teenth Saturday is the 13th, the first day of the teenth week`` () = meetup 2013 4 Week.Teenth DayOfWeek.Saturday |> should equal (DateTime(2013, 4, 13)) -[] +[] let ``When teenth Saturday is the 19th, the last day of the teenth week`` () = meetup 2013 10 Week.Teenth DayOfWeek.Saturday |> should equal (DateTime(2013, 10, 19)) -[] +[] let ``When teenth Sunday is the 19th, the last day of the teenth week`` () = meetup 2013 5 Week.Teenth DayOfWeek.Sunday |> should equal (DateTime(2013, 5, 19)) -[] +[] let ``When teenth Sunday is some day in the middle of the teenth week`` () = meetup 2013 6 Week.Teenth DayOfWeek.Sunday |> should equal (DateTime(2013, 6, 16)) -[] +[] let ``When teenth Sunday is the 13th, the first day of the teenth week`` () = meetup 2013 10 Week.Teenth DayOfWeek.Sunday |> should equal (DateTime(2013, 10, 13)) -[] +[] let ``When first Monday is some day in the middle of the first week`` () = meetup 2013 3 Week.First DayOfWeek.Monday |> should equal (DateTime(2013, 3, 4)) -[] +[] let ``When first Monday is the 1st, the first day of the first week`` () = meetup 2013 4 Week.First DayOfWeek.Monday |> should equal (DateTime(2013, 4, 1)) -[] +[] let ``When first Tuesday is the 7th, the last day of the first week`` () = meetup 2013 5 Week.First DayOfWeek.Tuesday |> should equal (DateTime(2013, 5, 7)) -[] +[] let ``When first Tuesday is some day in the middle of the first week`` () = meetup 2013 6 Week.First DayOfWeek.Tuesday |> should equal (DateTime(2013, 6, 4)) -[] +[] let ``When first Wednesday is some day in the middle of the first week`` () = meetup 2013 7 Week.First DayOfWeek.Wednesday |> should equal (DateTime(2013, 7, 3)) -[] +[] let ``When first Wednesday is the 7th, the last day of the first week`` () = meetup 2013 8 Week.First DayOfWeek.Wednesday |> should equal (DateTime(2013, 8, 7)) -[] +[] let ``When first Thursday is some day in the middle of the first week`` () = meetup 2013 9 Week.First DayOfWeek.Thursday |> should equal (DateTime(2013, 9, 5)) -[] +[] let ``When first Thursday is another day in the middle of the first week`` () = meetup 2013 10 Week.First DayOfWeek.Thursday |> should equal (DateTime(2013, 10, 3)) -[] +[] let ``When first Friday is the 1st, the first day of the first week`` () = meetup 2013 11 Week.First DayOfWeek.Friday |> should equal (DateTime(2013, 11, 1)) -[] +[] let ``When first Friday is some day in the middle of the first week`` () = meetup 2013 12 Week.First DayOfWeek.Friday |> should equal (DateTime(2013, 12, 6)) -[] +[] let ``When first Saturday is some day in the middle of the first week`` () = meetup 2013 1 Week.First DayOfWeek.Saturday |> should equal (DateTime(2013, 1, 5)) -[] +[] let ``When first Saturday is another day in the middle of the first week`` () = meetup 2013 2 Week.First DayOfWeek.Saturday |> should equal (DateTime(2013, 2, 2)) -[] +[] let ``When first Sunday is some day in the middle of the first week`` () = meetup 2013 3 Week.First DayOfWeek.Sunday |> should equal (DateTime(2013, 3, 3)) -[] +[] let ``When first Sunday is the 7th, the last day of the first week`` () = meetup 2013 4 Week.First DayOfWeek.Sunday |> should equal (DateTime(2013, 4, 7)) -[] +[] let ``When second Monday is some day in the middle of the second week`` () = meetup 2013 3 Week.Second DayOfWeek.Monday |> should equal (DateTime(2013, 3, 11)) -[] +[] let ``When second Monday is the 8th, the first day of the second week`` () = meetup 2013 4 Week.Second DayOfWeek.Monday |> should equal (DateTime(2013, 4, 8)) -[] +[] let ``When second Tuesday is the 14th, the last day of the second week`` () = meetup 2013 5 Week.Second DayOfWeek.Tuesday |> should equal (DateTime(2013, 5, 14)) -[] +[] let ``When second Tuesday is some day in the middle of the second week`` () = meetup 2013 6 Week.Second DayOfWeek.Tuesday |> should equal (DateTime(2013, 6, 11)) -[] +[] let ``When second Wednesday is some day in the middle of the second week`` () = meetup 2013 7 Week.Second DayOfWeek.Wednesday |> should equal (DateTime(2013, 7, 10)) -[] +[] let ``When second Wednesday is the 14th, the last day of the second week`` () = meetup 2013 8 Week.Second DayOfWeek.Wednesday |> should equal (DateTime(2013, 8, 14)) -[] +[] let ``When second Thursday is some day in the middle of the second week`` () = meetup 2013 9 Week.Second DayOfWeek.Thursday |> should equal (DateTime(2013, 9, 12)) -[] +[] let ``When second Thursday is another day in the middle of the second week`` () = meetup 2013 10 Week.Second DayOfWeek.Thursday |> should equal (DateTime(2013, 10, 10)) -[] +[] let ``When second Friday is the 8th, the first day of the second week`` () = meetup 2013 11 Week.Second DayOfWeek.Friday |> should equal (DateTime(2013, 11, 8)) -[] +[] let ``When second Friday is some day in the middle of the second week`` () = meetup 2013 12 Week.Second DayOfWeek.Friday |> should equal (DateTime(2013, 12, 13)) -[] +[] let ``When second Saturday is some day in the middle of the second week`` () = meetup 2013 1 Week.Second DayOfWeek.Saturday |> should equal (DateTime(2013, 1, 12)) -[] +[] let ``When second Saturday is another day in the middle of the second week`` () = meetup 2013 2 Week.Second DayOfWeek.Saturday |> should equal (DateTime(2013, 2, 9)) -[] +[] let ``When second Sunday is some day in the middle of the second week`` () = meetup 2013 3 Week.Second DayOfWeek.Sunday |> should equal (DateTime(2013, 3, 10)) -[] +[] let ``When second Sunday is the 14th, the last day of the second week`` () = meetup 2013 4 Week.Second DayOfWeek.Sunday |> should equal (DateTime(2013, 4, 14)) -[] +[] let ``When third Monday is some day in the middle of the third week`` () = meetup 2013 3 Week.Third DayOfWeek.Monday |> should equal (DateTime(2013, 3, 18)) -[] +[] let ``When third Monday is the 15th, the first day of the third week`` () = meetup 2013 4 Week.Third DayOfWeek.Monday |> should equal (DateTime(2013, 4, 15)) -[] +[] let ``When third Tuesday is the 21st, the last day of the third week`` () = meetup 2013 5 Week.Third DayOfWeek.Tuesday |> should equal (DateTime(2013, 5, 21)) -[] +[] let ``When third Tuesday is some day in the middle of the third week`` () = meetup 2013 6 Week.Third DayOfWeek.Tuesday |> should equal (DateTime(2013, 6, 18)) -[] +[] let ``When third Wednesday is some day in the middle of the third week`` () = meetup 2013 7 Week.Third DayOfWeek.Wednesday |> should equal (DateTime(2013, 7, 17)) -[] +[] let ``When third Wednesday is the 21st, the last day of the third week`` () = meetup 2013 8 Week.Third DayOfWeek.Wednesday |> should equal (DateTime(2013, 8, 21)) -[] +[] let ``When third Thursday is some day in the middle of the third week`` () = meetup 2013 9 Week.Third DayOfWeek.Thursday |> should equal (DateTime(2013, 9, 19)) -[] +[] let ``When third Thursday is another day in the middle of the third week`` () = meetup 2013 10 Week.Third DayOfWeek.Thursday |> should equal (DateTime(2013, 10, 17)) -[] +[] let ``When third Friday is the 15th, the first day of the third week`` () = meetup 2013 11 Week.Third DayOfWeek.Friday |> should equal (DateTime(2013, 11, 15)) -[] +[] let ``When third Friday is some day in the middle of the third week`` () = meetup 2013 12 Week.Third DayOfWeek.Friday |> should equal (DateTime(2013, 12, 20)) -[] +[] let ``When third Saturday is some day in the middle of the third week`` () = meetup 2013 1 Week.Third DayOfWeek.Saturday |> should equal (DateTime(2013, 1, 19)) -[] +[] let ``When third Saturday is another day in the middle of the third week`` () = meetup 2013 2 Week.Third DayOfWeek.Saturday |> should equal (DateTime(2013, 2, 16)) -[] +[] let ``When third Sunday is some day in the middle of the third week`` () = meetup 2013 3 Week.Third DayOfWeek.Sunday |> should equal (DateTime(2013, 3, 17)) -[] +[] let ``When third Sunday is the 21st, the last day of the third week`` () = meetup 2013 4 Week.Third DayOfWeek.Sunday |> should equal (DateTime(2013, 4, 21)) -[] +[] let ``When fourth Monday is some day in the middle of the fourth week`` () = meetup 2013 3 Week.Fourth DayOfWeek.Monday |> should equal (DateTime(2013, 3, 25)) -[] +[] let ``When fourth Monday is the 22nd, the first day of the fourth week`` () = meetup 2013 4 Week.Fourth DayOfWeek.Monday |> should equal (DateTime(2013, 4, 22)) -[] +[] let ``When fourth Tuesday is the 28th, the last day of the fourth week`` () = meetup 2013 5 Week.Fourth DayOfWeek.Tuesday |> should equal (DateTime(2013, 5, 28)) -[] +[] let ``When fourth Tuesday is some day in the middle of the fourth week`` () = meetup 2013 6 Week.Fourth DayOfWeek.Tuesday |> should equal (DateTime(2013, 6, 25)) -[] +[] let ``When fourth Wednesday is some day in the middle of the fourth week`` () = meetup 2013 7 Week.Fourth DayOfWeek.Wednesday |> should equal (DateTime(2013, 7, 24)) -[] +[] let ``When fourth Wednesday is the 28th, the last day of the fourth week`` () = meetup 2013 8 Week.Fourth DayOfWeek.Wednesday |> should equal (DateTime(2013, 8, 28)) -[] +[] let ``When fourth Thursday is some day in the middle of the fourth week`` () = meetup 2013 9 Week.Fourth DayOfWeek.Thursday |> should equal (DateTime(2013, 9, 26)) -[] +[] let ``When fourth Thursday is another day in the middle of the fourth week`` () = meetup 2013 10 Week.Fourth DayOfWeek.Thursday |> should equal (DateTime(2013, 10, 24)) -[] +[] let ``When fourth Friday is the 22nd, the first day of the fourth week`` () = meetup 2013 11 Week.Fourth DayOfWeek.Friday |> should equal (DateTime(2013, 11, 22)) -[] +[] let ``When fourth Friday is some day in the middle of the fourth week`` () = meetup 2013 12 Week.Fourth DayOfWeek.Friday |> should equal (DateTime(2013, 12, 27)) -[] +[] let ``When fourth Saturday is some day in the middle of the fourth week`` () = meetup 2013 1 Week.Fourth DayOfWeek.Saturday |> should equal (DateTime(2013, 1, 26)) -[] +[] let ``When fourth Saturday is another day in the middle of the fourth week`` () = meetup 2013 2 Week.Fourth DayOfWeek.Saturday |> should equal (DateTime(2013, 2, 23)) -[] +[] let ``When fourth Sunday is some day in the middle of the fourth week`` () = meetup 2013 3 Week.Fourth DayOfWeek.Sunday |> should equal (DateTime(2013, 3, 24)) -[] +[] let ``When fourth Sunday is the 28th, the last day of the fourth week`` () = meetup 2013 4 Week.Fourth DayOfWeek.Sunday |> should equal (DateTime(2013, 4, 28)) -[] +[] let ``Last Monday in a month with four Mondays`` () = meetup 2013 3 Week.Last DayOfWeek.Monday |> should equal (DateTime(2013, 3, 25)) -[] +[] let ``Last Monday in a month with five Mondays`` () = meetup 2013 4 Week.Last DayOfWeek.Monday |> should equal (DateTime(2013, 4, 29)) -[] +[] let ``Last Tuesday in a month with four Tuesdays`` () = meetup 2013 5 Week.Last DayOfWeek.Tuesday |> should equal (DateTime(2013, 5, 28)) -[] +[] let ``Last Tuesday in another month with four Tuesdays`` () = meetup 2013 6 Week.Last DayOfWeek.Tuesday |> should equal (DateTime(2013, 6, 25)) -[] +[] let ``Last Wednesday in a month with five Wednesdays`` () = meetup 2013 7 Week.Last DayOfWeek.Wednesday |> should equal (DateTime(2013, 7, 31)) -[] +[] let ``Last Wednesday in a month with four Wednesdays`` () = meetup 2013 8 Week.Last DayOfWeek.Wednesday |> should equal (DateTime(2013, 8, 28)) -[] +[] let ``Last Thursday in a month with four Thursdays`` () = meetup 2013 9 Week.Last DayOfWeek.Thursday |> should equal (DateTime(2013, 9, 26)) -[] +[] let ``Last Thursday in a month with five Thursdays`` () = meetup 2013 10 Week.Last DayOfWeek.Thursday |> should equal (DateTime(2013, 10, 31)) -[] +[] let ``Last Friday in a month with five Fridays`` () = meetup 2013 11 Week.Last DayOfWeek.Friday |> should equal (DateTime(2013, 11, 29)) -[] +[] let ``Last Friday in a month with four Fridays`` () = meetup 2013 12 Week.Last DayOfWeek.Friday |> should equal (DateTime(2013, 12, 27)) -[] +[] let ``Last Saturday in a month with four Saturdays`` () = meetup 2013 1 Week.Last DayOfWeek.Saturday |> should equal (DateTime(2013, 1, 26)) -[] +[] let ``Last Saturday in another month with four Saturdays`` () = meetup 2013 2 Week.Last DayOfWeek.Saturday |> should equal (DateTime(2013, 2, 23)) -[] +[] let ``Last Sunday in a month with five Sundays`` () = meetup 2013 3 Week.Last DayOfWeek.Sunday |> should equal (DateTime(2013, 3, 31)) -[] +[] let ``Last Sunday in a month with four Sundays`` () = meetup 2013 4 Week.Last DayOfWeek.Sunday |> should equal (DateTime(2013, 4, 28)) -[] +[] let ``When last Wednesday in February in a leap year is the 29th`` () = meetup 2012 2 Week.Last DayOfWeek.Wednesday |> should equal (DateTime(2012, 2, 29)) -[] +[] let ``Last Wednesday in December that is also the last day of the year`` () = meetup 2014 12 Week.Last DayOfWeek.Wednesday |> should equal (DateTime(2014, 12, 31)) -[] +[] let ``When last Sunday in February in a non-leap year is not the 29th`` () = meetup 2015 2 Week.Last DayOfWeek.Sunday |> should equal (DateTime(2015, 2, 22)) -[] +[] let ``When first Friday is the 7th, the last day of the first week`` () = meetup 2012 12 Week.First DayOfWeek.Friday |> should equal (DateTime(2012, 12, 7)) - From db3df6adb569563f6f1b1c5dd0a85466012b89cb Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Wed, 21 Feb 2024 13:33:57 +0100 Subject: [PATCH 33/39] Meetup : Refacto --- exercises/practice/meetup/Meetup.fs | 61 ++++++++++------------------- 1 file changed, 21 insertions(+), 40 deletions(-) diff --git a/exercises/practice/meetup/Meetup.fs b/exercises/practice/meetup/Meetup.fs index f166a31de..1e96506f5 100644 --- a/exercises/practice/meetup/Meetup.fs +++ b/exercises/practice/meetup/Meetup.fs @@ -10,8 +10,8 @@ type Week = | Last | Teenth -let isTeenth = - function +let isTeenth(date: DateTime) = + match date.Day with | 13 | 14 | 15 @@ -21,45 +21,26 @@ let isTeenth = | 19 -> true | _ -> false -let countOk week count = - match week with - | First -> count = 1 - | Second -> count = 2 - | Third -> count = 3 - | Fourth -> count = 4 - | _ -> false - -let rec findDate (date: DateTime) week dayOfWeek count = - if date.DayOfWeek = dayOfWeek then - match week with - | Teenth -> - if (date.Day |> isTeenth) then - date - else - (findDate (date.AddDays(1)) week dayOfWeek count) - | First - | Second - | Third - | Fourth -> - if countOk week count then - date - else - (findDate (date.AddDays(1)) week dayOfWeek (count + 1)) +let generateDates(startDate: DateTime) = + let endDate = startDate.AddMonths(1).AddDays(-1) - | Last -> date - else - findDate (date.AddDays(1)) week dayOfWeek count - -let rec findDateRev (date: DateTime) dayOfWeek = - if (date.DayOfWeek = dayOfWeek) then - date - else - findDateRev (date.AddDays(-1)) dayOfWeek + startDate + |> Array.unfold (fun date -> + if date <= endDate then + Some(date, date.AddDays(1)) + else + None) let meetup year month week dayOfWeek : DateTime = - let date = DateTime(year, month, 1) + let dates = + DateTime(year, month, 1) + |> generateDates + |> Array.filter (fun d -> d.DayOfWeek = dayOfWeek) - if (week = Last) then - findDateRev (date.AddMonths(1).AddDays(-1)) dayOfWeek - else - findDate date week dayOfWeek 1 + match week with + | First -> dates |> Array.head + | Second -> dates[1] + | Third -> dates[2] + | Fourth -> dates[3] + | Last -> dates |> Array.last + | Teenth -> dates |> Array.find isTeenth From 1f5fff30e185daa34d49faf098de7b9e172d0865 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Wed, 21 Feb 2024 13:52:47 +0100 Subject: [PATCH 34/39] Meetup : refacto --- exercises/practice/meetup/Meetup.fs | 30 +++++++---------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/exercises/practice/meetup/Meetup.fs b/exercises/practice/meetup/Meetup.fs index 1e96506f5..0dc01d69e 100644 --- a/exercises/practice/meetup/Meetup.fs +++ b/exercises/practice/meetup/Meetup.fs @@ -12,35 +12,19 @@ type Week = let isTeenth(date: DateTime) = match date.Day with - | 13 - | 14 - | 15 - | 16 - | 17 - | 18 - | 19 -> true + | d when d >= 13 && d <= 19 -> true | _ -> false -let generateDates(startDate: DateTime) = - let endDate = startDate.AddMonths(1).AddDays(-1) - - startDate - |> Array.unfold (fun date -> - if date <= endDate then - Some(date, date.AddDays(1)) - else - None) - let meetup year month week dayOfWeek : DateTime = let dates = - DateTime(year, month, 1) - |> generateDates - |> Array.filter (fun d -> d.DayOfWeek = dayOfWeek) + [ 1 .. DateTime.DaysInMonth(year, month) ] + |> List.map (fun day -> DateTime(year, month, day)) + |> List.filter (fun d -> d.DayOfWeek = dayOfWeek) match week with - | First -> dates |> Array.head + | First -> dates |> List.head | Second -> dates[1] | Third -> dates[2] | Fourth -> dates[3] - | Last -> dates |> Array.last - | Teenth -> dates |> Array.find isTeenth + | Last -> dates |> List.last + | Teenth -> dates |> List.find isTeenth From 3e76fab36e3e14ba9eaa6ca518ffd34feb1efeda Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Wed, 21 Feb 2024 14:35:10 +0100 Subject: [PATCH 35/39] AffineCipher : Start solving --- .../practice/affine-cipher/AffineCipher.fs | 27 +++++++++++++++++-- .../affine-cipher/AffineCipherTests.fs | 17 ++++++------ 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/exercises/practice/affine-cipher/AffineCipher.fs b/exercises/practice/affine-cipher/AffineCipher.fs index 017945a56..8d7bb982b 100644 --- a/exercises/practice/affine-cipher/AffineCipher.fs +++ b/exercises/practice/affine-cipher/AffineCipher.fs @@ -1,5 +1,28 @@ module AffineCipher -let decode a b cipheredText = failwith "You need to implement this function." +open System -let encode a b plainText = failwith "You need to implement this function." +let alphabet = [ 'a' .. 'z' ] + +let decode a b cipheredText = + failwith "You need to implement this function." + +let encodeChar a b c = + if (c |> Char.IsDigit) then + c + else + let i = alphabet |> List.findIndex ((=) c) + let result = (a * i + b) % 26 + alphabet[result] + +let encode a b (plainText: string) = + plainText.ToLower().Replace(" ", "") + |> Seq.choose (fun c -> + if (c |> Char.IsLetterOrDigit) then + Some(encodeChar a b c) + else + None) + |> Seq.chunkBySize 5 + |> Seq.map String.Concat + |> (function + | s -> String.Join(" ", s)) diff --git a/exercises/practice/affine-cipher/AffineCipherTests.fs b/exercises/practice/affine-cipher/AffineCipherTests.fs index 4d47fd030..e8ea090e9 100644 --- a/exercises/practice/affine-cipher/AffineCipherTests.fs +++ b/exercises/practice/affine-cipher/AffineCipherTests.fs @@ -9,35 +9,35 @@ open AffineCipher let ``Encode yes`` () = encode 5 7 "yes" |> should equal "xbt" -[] +[] let ``Encode no`` () = encode 15 18 "no" |> should equal "fu" -[] +[] let ``Encode OMG`` () = encode 21 3 "OMG" |> should equal "lvz" -[] +[] let ``Encode O M G`` () = encode 25 47 "O M G" |> should equal "hjp" -[] +[] let ``Encode mindblowingly`` () = encode 11 15 "mindblowingly" |> should equal "rzcwa gnxzc dgt" -[] +[] let ``Encode numbers`` () = encode 3 4 "Testing,1 2 3, testing." |> should equal "jqgjc rw123 jqgjc rw" -[] +[] let ``Encode deep thought`` () = encode 5 17 "Truth is fiction." |> should equal "iynia fdqfb ifje" -[] +[] let ``Encode all the letters`` () = encode 17 33 "The quick brown fox jumps over the lazy dog." |> should equal "swxtj npvyk lruol iejdc blaxk swxmh qzglf" -[] +[] let ``Encode with a not coprime to m`` () = (fun () -> encode 6 17 "This is a test." |> ignore) |> should throw typeof @@ -68,4 +68,3 @@ let ``Decode with too many spaces`` () = [] let ``Decode with a not coprime to m`` () = (fun () -> decode 13 5 "Test" |> ignore) |> should throw typeof - From f1b3fe3cc1563098b80799260a2e372fce347d4a Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Wed, 21 Feb 2024 15:42:18 +0100 Subject: [PATCH 36/39] Check coprime --- .../practice/affine-cipher/AffineCipher.fs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/exercises/practice/affine-cipher/AffineCipher.fs b/exercises/practice/affine-cipher/AffineCipher.fs index 8d7bb982b..7c30f085d 100644 --- a/exercises/practice/affine-cipher/AffineCipher.fs +++ b/exercises/practice/affine-cipher/AffineCipher.fs @@ -4,8 +4,20 @@ open System let alphabet = [ 'a' .. 'z' ] +let rec gcd p q = + if (p = 0 || q = 0) then 0 + elif (p = q) then p + elif (p > q) then gcd (p - q) q + else gcd p (q - p) + let decode a b cipheredText = - failwith "You need to implement this function." + // D(y) = (a^-1)(y - b) mod m + // // cipheredText |> Seq.map (fun c -> + // // let y = alphabet |> List.findIndex ((=) c) + // // let result = (y - b) % 26 + // + // ) + "" let encodeChar a b c = if (c |> Char.IsDigit) then @@ -16,6 +28,9 @@ let encodeChar a b c = alphabet[result] let encode a b (plainText: string) = + if (gcd a b = 1) then + raise (ArgumentException "Not coprime") + plainText.ToLower().Replace(" ", "") |> Seq.choose (fun c -> if (c |> Char.IsLetterOrDigit) then From 91f2a2a68024a33879d026777f8717318d54dbe5 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Thu, 22 Feb 2024 14:41:17 +0100 Subject: [PATCH 37/39] Solve AffineCipher. --- .../practice/affine-cipher/AffineCipher.fs | 44 +++++++++++++++---- .../affine-cipher/AffineCipherTests.fs | 14 +++--- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/exercises/practice/affine-cipher/AffineCipher.fs b/exercises/practice/affine-cipher/AffineCipher.fs index 7c30f085d..d1e44bbf8 100644 --- a/exercises/practice/affine-cipher/AffineCipher.fs +++ b/exercises/practice/affine-cipher/AffineCipher.fs @@ -4,20 +4,46 @@ open System let alphabet = [ 'a' .. 'z' ] +let private coprime a b = + let rec gcd a b = + if a = b then a + elif a > b then gcd (a - b) b + else gcd a (b - a) + + gcd a b = 1 + +let private mmi a m = + let rec loop a x m = + match a * x % m with + | 1 -> x + | _ -> loop a (x + 1) m + + loop a 1 m + let rec gcd p q = if (p = 0 || q = 0) then 0 elif (p = q) then p elif (p > q) then gcd (p - q) q else gcd p (q - p) -let decode a b cipheredText = - // D(y) = (a^-1)(y - b) mod m - // // cipheredText |> Seq.map (fun c -> - // // let y = alphabet |> List.findIndex ((=) c) - // // let result = (y - b) % 26 - // - // ) - "" +let private (%%) n m = + match n % m with + | mod' when sign mod' >= 0 -> mod' + | mod' -> abs m + mod' + +let decode a b (cipheredText: string) = + if (coprime a 26 |> not) then + raise (ArgumentException "Not coprime") + + cipheredText.ToLower().Replace(" ", "") + |> Seq.map (fun c -> + if c |> Char.IsDigit then + c + else + let y = alphabet |> List.findIndex ((=) c) + let result = (mmi a 26) * (y - b) %% 26 + alphabet[result]) + |> String.Concat let encodeChar a b c = if (c |> Char.IsDigit) then @@ -28,7 +54,7 @@ let encodeChar a b c = alphabet[result] let encode a b (plainText: string) = - if (gcd a b = 1) then + if (coprime a 26 |> not) then raise (ArgumentException "Not coprime") plainText.ToLower().Replace(" ", "") diff --git a/exercises/practice/affine-cipher/AffineCipherTests.fs b/exercises/practice/affine-cipher/AffineCipherTests.fs index e8ea090e9..5d75fbbc2 100644 --- a/exercises/practice/affine-cipher/AffineCipherTests.fs +++ b/exercises/practice/affine-cipher/AffineCipherTests.fs @@ -41,30 +41,30 @@ let ``Encode all the letters`` () = let ``Encode with a not coprime to m`` () = (fun () -> encode 6 17 "This is a test." |> ignore) |> should throw typeof -[] +[] let ``Decode exercism`` () = decode 3 7 "tytgn fjr" |> should equal "exercism" -[] +[] let ``Decode a sentence`` () = decode 19 16 "qdwju nqcro muwhn odqun oppmd aunwd o" |> should equal "anobstacleisoftenasteppingstone" -[] +[] let ``Decode numbers`` () = decode 25 7 "odpoz ub123 odpoz ub" |> should equal "testing123testing" -[] +[] let ``Decode all the letters`` () = decode 17 33 "swxtj npvyk lruol iejdc blaxk swxmh qzglf" |> should equal "thequickbrownfoxjumpsoverthelazydog" -[] +[] let ``Decode with no spaces in input`` () = decode 17 33 "swxtjnpvyklruoliejdcblaxkswxmhqzglf" |> should equal "thequickbrownfoxjumpsoverthelazydog" -[] +[] let ``Decode with too many spaces`` () = decode 15 16 "vszzm cly yd cg qdp" |> should equal "jollygreengiant" -[] +[] let ``Decode with a not coprime to m`` () = (fun () -> decode 13 5 "Test" |> ignore) |> should throw typeof From 1e7de35a94550a0c4e6e0a1ccf219b22750345f7 Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Thu, 6 Jun 2024 14:33:32 +0200 Subject: [PATCH 38/39] ListOps --- exercises/practice/list-ops/ListOps.fs | 29 +++++++++++++++------ exercises/practice/list-ops/ListOpsTests.fs | 5 ++-- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/exercises/practice/list-ops/ListOps.fs b/exercises/practice/list-ops/ListOps.fs index 2d900a15c..796271d5b 100644 --- a/exercises/practice/list-ops/ListOps.fs +++ b/exercises/practice/list-ops/ListOps.fs @@ -1,17 +1,30 @@ module ListOps -let rec foldl folder state list = failwith "You need to implement this function." +let rec foldl folder state list = + failwith "You need to implement this function." -let rec foldr folder state list = failwith "You need to implement this function." +let rec foldr folder state list = + failwith "You need to implement this function." -let length list = failwith "You need to implement this function." +let length list = + let mutable i = 0 -let reverse list = failwith "You need to implement this function." + for _ in list do + i <- i + 1 -let map f list = failwith "You need to implement this function." + i -let filter f list = failwith "You need to implement this function." +let reverse list = + failwith "You need to implement this function." -let append xs ys = failwith "You need to implement this function." +let map f list = + failwith "You need to implement this function." -let concat xs = failwith "You need to implement this function." \ No newline at end of file +let filter f list = + failwith "You need to implement this function." + +let append xs ys = + failwith "You need to implement this function." + +let concat xs = + failwith "You need to implement this function." diff --git a/exercises/practice/list-ops/ListOpsTests.fs b/exercises/practice/list-ops/ListOpsTests.fs index 849b2d016..aab3b333e 100644 --- a/exercises/practice/list-ops/ListOpsTests.fs +++ b/exercises/practice/list-ops/ListOpsTests.fs @@ -41,11 +41,11 @@ let ``filter empty list`` () = let ``filter non-empty list`` () = filter (fun acc -> acc % 2 = 1) [1; 2; 3; 5] |> should equal [1; 3; 5] -[] +[] let ``length empty list`` () = length [] |> should equal 0 -[] +[] let ``length non-empty list`` () = length [1; 2; 3; 4] |> should equal 4 @@ -84,4 +84,3 @@ let ``reverse non-empty list`` () = [] let ``reverse list of lists is not flattened`` () = reverse [[1; 2]; [3]; []; [4; 5; 6]] |> should equal [[4; 5; 6]; []; [3]; [1; 2]] - From 4a4ccbb31654b462c8ea3abdd9130959fa7177cb Mon Sep 17 00:00:00 2001 From: Vincent DELCOIGNE Date: Tue, 25 Jun 2024 13:44:28 +0200 Subject: [PATCH 39/39] Solve RNA transcript --- .../practice/rna-transcription/RnaTranscription.fs | 12 +++++++++++- .../rna-transcription/RnaTranscriptionTests.fs | 11 +++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/exercises/practice/rna-transcription/RnaTranscription.fs b/exercises/practice/rna-transcription/RnaTranscription.fs index 8933d02b7..e590a3d2d 100644 --- a/exercises/practice/rna-transcription/RnaTranscription.fs +++ b/exercises/practice/rna-transcription/RnaTranscription.fs @@ -1,3 +1,13 @@ module RnaTranscription -let toRna (dna: string): string = failwith "You need to implement this function." \ No newline at end of file +open System + +let transform = function + | 'G' -> 'C' + | 'C' -> 'G' + | 'T' -> 'A' + | 'A' -> 'U' + | _ -> failwith "Invalid DNA" + +let toRna dna = + dna |> Seq.map transform |> String.Concat diff --git a/exercises/practice/rna-transcription/RnaTranscriptionTests.fs b/exercises/practice/rna-transcription/RnaTranscriptionTests.fs index c87333d47..37523c00f 100644 --- a/exercises/practice/rna-transcription/RnaTranscriptionTests.fs +++ b/exercises/practice/rna-transcription/RnaTranscriptionTests.fs @@ -9,23 +9,22 @@ open RnaTranscription let ``Empty RNA sequence`` () = toRna "" |> should equal "" -[] +[] let ``RNA complement of cytosine is guanine`` () = toRna "C" |> should equal "G" -[] +[] let ``RNA complement of guanine is cytosine`` () = toRna "G" |> should equal "C" -[] +[] let ``RNA complement of thymine is adenine`` () = toRna "T" |> should equal "A" -[] +[] let ``RNA complement of adenine is uracil`` () = toRna "A" |> should equal "U" -[] +[] let ``RNA complement`` () = toRna "ACGTGGTCTTAA" |> should equal "UGCACCAGAAUU" -