Wrapped and done

This commit is contained in:
eeva 2018-01-27 16:43:16 +02:00
parent 476f7e4356
commit 47ee21da0a
3 changed files with 79 additions and 46 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "ep0002"]
path = ep0002
url = https://github.com/MasseR/katas.git

View File

@ -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

@ -0,0 +1 @@
Subproject commit 859fdd499ee97d486846d621235035b2c7ea4383