Wrapped and done
This commit is contained in:
parent
476f7e4356
commit
47ee21da0a
3 changed files with 79 additions and 46 deletions
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "ep0002"]
|
||||||
|
path = ep0002
|
||||||
|
url = https://github.com/MasseR/katas.git
|
|
@ -4,7 +4,29 @@
|
||||||
|
|
||||||
(Inspired from this exercice: http://www.codewars.com/kata/54dc6f5a224c26032800005c/train/haskell)
|
(Inspired from this exercice: http://www.codewars.com/kata/54dc6f5a224c26032800005c/train/haskell)
|
||||||
|
|
||||||
## Introduction
|
## REPL compatible copy/paste (TL;DR)
|
||||||
|
|
||||||
|
```
|
||||||
|
import Dict
|
||||||
|
|
||||||
|
type Stock = Stock { name : String, count : Int }
|
||||||
|
library1 = [ Stock { name = "ABART", count = 20 } , Stock { name = "CDXEF", count = 50 } , Stock { name = "BKWRK", count = 25 } , Stock { name = "BTSQZ", count = 89 } , Stock { name = "DRTYM", count = 60 } ]
|
||||||
|
categories1 = [ 'A', 'B', 'C', 'W' ]
|
||||||
|
|
||||||
|
toUplet : List Repl.Stock -> List ( String, Int )
|
||||||
|
toUplet = List.map (\x -> case x of Stock v -> (v.name,v.count))
|
||||||
|
|
||||||
|
onlyInitials : List (String, Int) -> List (Char, Int)
|
||||||
|
onlyInitials = List.map (\x -> case x of (s, i) -> ((Maybe.withDefault '\0' << List.head << String.toList) s,i) )
|
||||||
|
|
||||||
|
howMany : Char -> List Char Int -> Int
|
||||||
|
howMany c = (List.foldl (\t acc -> acc + Tuple.second t) 0) << List.filter (\t -> (Tuple.first t) == c)
|
||||||
|
|
||||||
|
stockList : List Stock -> List Char -> List ( Char, Int )
|
||||||
|
stockList sl = List.map (\c -> (c, sl |> toUplet >> onlyInitials >> howMany c))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Prologue
|
||||||
|
|
||||||
You are stuck in a library with a very talkative and experimental science-minded IA. It keeps repeating the following:
|
You are stuck in a library with a very talkative and experimental science-minded IA. It keeps repeating the following:
|
||||||
|
|
||||||
|
@ -28,7 +50,8 @@ Since you would really like to have peace, you dare ask:
|
||||||
|
|
||||||
> — What code? What are you talking about?
|
> — What code? What are you talking about?
|
||||||
|
|
||||||
> — I really need to sort the library index and count how many books have the same first-letter reference, but only in from a list…
|
> — I really need to sort the library index and count how many books have the
|
||||||
|
> same first-letter reference, but only in from a list…
|
||||||
|
|
||||||
> — Wait!
|
> — Wait!
|
||||||
|
|
||||||
|
@ -36,7 +59,10 @@ Since you would really like to have peace, you dare ask:
|
||||||
|
|
||||||
> This makes no sense, can't you write it down?
|
> This makes no sense, can't you write it down?
|
||||||
|
|
||||||
A few seconds pass in total silence. Then, there's a sound coming from your right, something like an inkjet printer work at a fast pace. It's actually a printer. It throws the printed sheet of paper at you in a furious “TWEEEEEEEEEEEP”. You pick up the paper and read:
|
A few seconds pass in total silence. Then, there's a sound coming from
|
||||||
|
your right, something like an inkjet printer work at a fast pace. It's
|
||||||
|
actually a printer. It throws the printed sheet of paper at you in a furious
|
||||||
|
“TWEEEEEEEEEEEP”. You pick up the paper and read:
|
||||||
|
|
||||||
INPUT................:
|
INPUT................:
|
||||||
LIBRARY COUNT......: [("ABOOK", 12), ("BPDEXU", 45), ("BJLDRSA", 15), ("CLDAVB", 98), ("DAJLDI", 32)]
|
LIBRARY COUNT......: [("ABOOK", 12), ("BPDEXU", 45), ("BJLDRSA", 15), ("CLDAVB", 98), ("DAJLDI", 32)]
|
||||||
|
@ -104,60 +130,63 @@ main =
|
||||||
|
|
||||||
## Walkthrough
|
## Walkthrough
|
||||||
|
|
||||||
Ok, so you'll have to fill the blank. After a few minutes, you identify two
|
All is needed is to fill the gaps from `library1`, `categories1` to `stockList`.
|
||||||
main components: one is about taking the first letter of every `Stock` name. The
|
So, what is needed for successfully building a stockList? Let's look at the types:
|
||||||
second is finding the amount of each `Char` in a `List (Char, Int)`.
|
We have a `List Stock` but we don't have any function to work on it. We could
|
||||||
>
|
make some functions for this, but we'll just be lazy and turn a List Stock into
|
||||||
Let's start with the second point and assume we have a value of `List (Char,
|
a much lower level `List (String, Int)`:
|
||||||
Int)` already available. We know at least to functions to recurse on lists:
|
|
||||||
maps and folds. Clearly, we'll have to choose a fold, since some aggregating is
|
|
||||||
needed when several identical `Char` are in the list. Let's pick foldl, it does
|
|
||||||
not really matter right now. So, the type of `List.foldl` is:
|
|
||||||
|
|
||||||
foldl : (a -> b -> b) -> b -> List a -> b
|
toUplet : List Stock -> List ( String, Int )
|
||||||
|
toUplet = List.map (\x -> case x of Stock v -> (v.name,v.count))
|
||||||
|
|
||||||
In fact, you notice you'll need two folds! One inner fold for counting of many
|
We'll need to count stock by initials, so we write a function for that:
|
||||||
book for initial X, and one outer fold for going through all the initials
|
|
||||||
required by the master.
|
|
||||||
|
|
||||||
### Inner foldl: how many books with Initial X
|
onlyInitials : List (String, Int) -> List (Char, Int)
|
||||||
|
onlyInitials = List.map (\x -> case x of (s, i) -> ((Maybe.withDefault '\0' << List.head << String.toList) s,i) )
|
||||||
|
|
||||||
You think about using type aliases to make your code easier to read, the IA
|
So, for every tuple, we operate of the left part. There is some strange
|
||||||
won't mind. To specialize your inner foldl, which is morally a foldlForX, you
|
machinery for turning a `String -> Char` involving a Maybe value, because
|
||||||
identify `a` as an element of a library stock (even though the title has been
|
`List.head` may fail. Of course in our case, no book should ever have an empty
|
||||||
shortened already to its initial), and `b` as the final index count.
|
title, but Elm doesn't know that. We mitigate this by setting a default `null`
|
||||||
|
char if the title is empty.
|
||||||
|
|
||||||
type alias Initial = Char
|
Good! We can already chain the two preceding functions since the output type
|
||||||
type alias Count = Int
|
of `toUplet` matches the input type of `onlyInitials`. Ain't that a coincidence?
|
||||||
type alias TitleCount = (Initial, Int)
|
|
||||||
type alias FinalCount = (Initial, Int)
|
|
||||||
|
|
||||||
You rewrite your specialized foldl type signature to account for the type
|
> library1 |> toUplet >> onlyInitials
|
||||||
aliases:
|
[('A',20),('C',50),('B',25),('B',89),('D',60)] : List ( Char, Int )
|
||||||
|
|
||||||
foldForX : (TitleCount -> FinalCount -> FinalCount)
|
Oh, but oh. There are two Bs in there! Those should go together and their counts
|
||||||
-> FinalCount
|
added together, and for that, nothing is better than a fold. Let's write a function
|
||||||
-> List TitleCount
|
that answers the question: “How many X is there?” where X is a Char.
|
||||||
-> FinalCount
|
|
||||||
|
|
||||||
After a call to this, you'll get how many books have a title that starts with
|
howMany : Char -> List Char Int -> Int
|
||||||
the initial X.
|
howMany c = List.filter (\t -> (Tuple.first t) == c)
|
||||||
|
>> (List.foldl (\t acc -> acc + Tuple.second t) 0)
|
||||||
|
|
||||||
You are missing the function with type `(TitleCount -> FinalCount ->
|
`howMany` is made of two functions: the first is one that only keeps tuples
|
||||||
FinalCount)`, and you reflect a little bit on its name and on its usage. Hmmm,
|
of interest (they match our Char c) and then we blindly sum the count of the
|
||||||
it takes a TitleCount and a FinalCount and returns a (new) FinalCount? You
|
resulting list. We always start the count from 0, so that if we don't find
|
||||||
choose to call it:
|
anything, it's still correct.
|
||||||
|
|
||||||
updateFinalCount : (TitleCount -> FinalCount -> FinalCount)
|
Let's try to find how may books that have a title starting with B in the library:
|
||||||
|
|
||||||
Actually it takes another (hidden) parameter, you change its name to reflect
|
> library1 |> toUplet >> onlyInitials >> howMany 'B'
|
||||||
that:
|
114 : Int
|
||||||
|
|
||||||
|
Nice! Well, we're almost done actually. We just need to do that for every letter
|
||||||
|
we were requested in `categories1`. Let's use a map for that:
|
||||||
|
|
||||||
|
stockList sl = List.map (\c -> (c, sl |> toUplet >> onlyInitials >> howMany c) )
|
||||||
|
|
||||||
|
And voilà! Let's run this:
|
||||||
|
|
||||||
|
> stockList library1 categories1
|
||||||
|
[('A',20),('B',114),('C',50),('W',0)] : List ( Char, Int )
|
||||||
|
|
||||||
|
Mission accomplished.
|
||||||
|
|
||||||
|
## Epilogue
|
||||||
|
|
||||||
updateFinalCountForX : (TitleCount -> FinalCount -> FinalCount)
|
|
||||||
updateFinalCountForX t f =
|
|
||||||
|
|
||||||
|
|
||||||
NB:
|
|
||||||
addBio : String -> User -> User
|
|
||||||
addBio bio user =
|
|
||||||
{ user | bio = bio }
|
|
||||||
|
|
1
ep0002
Submodule
1
ep0002
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 859fdd499ee97d486846d621235035b2c7ea4383
|
Loading…
Reference in a new issue