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)
|
||||
|
||||
## 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:
|
||||
|
||||
|
@ -28,7 +50,8 @@ Since you would really like to have peace, you dare ask:
|
|||
|
||||
> — 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!
|
||||
|
||||
|
@ -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?
|
||||
|
||||
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................:
|
||||
LIBRARY COUNT......: [("ABOOK", 12), ("BPDEXU", 45), ("BJLDRSA", 15), ("CLDAVB", 98), ("DAJLDI", 32)]
|
||||
|
@ -104,60 +130,63 @@ main =
|
|||
|
||||
## Walkthrough
|
||||
|
||||
Ok, so you'll have to fill the blank. After a few minutes, you identify two
|
||||
main components: one is about taking the first letter of every `Stock` name. The
|
||||
second is finding the amount of each `Char` in a `List (Char, Int)`.
|
||||
>
|
||||
Let's start with the second point and assume we have a value of `List (Char,
|
||||
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:
|
||||
All is needed is to fill the gaps from `library1`, `categories1` to `stockList`.
|
||||
So, what is needed for successfully building a stockList? Let's look at the types:
|
||||
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
|
||||
a much lower level `List (String, Int)`:
|
||||
|
||||
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
|
||||
book for initial X, and one outer fold for going through all the initials
|
||||
required by the master.
|
||||
We'll need to count stock by initials, so we write a function for that:
|
||||
|
||||
### 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
|
||||
won't mind. To specialize your inner foldl, which is morally a foldlForX, you
|
||||
identify `a` as an element of a library stock (even though the title has been
|
||||
shortened already to its initial), and `b` as the final index count.
|
||||
So, for every tuple, we operate of the left part. There is some strange
|
||||
machinery for turning a `String -> Char` involving a Maybe value, because
|
||||
`List.head` may fail. Of course in our case, no book should ever have an empty
|
||||
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
|
||||
type alias Count = Int
|
||||
type alias TitleCount = (Initial, Int)
|
||||
type alias FinalCount = (Initial, Int)
|
||||
Good! We can already chain the two preceding functions since the output type
|
||||
of `toUplet` matches the input type of `onlyInitials`. Ain't that a coincidence?
|
||||
|
||||
You rewrite your specialized foldl type signature to account for the type
|
||||
aliases:
|
||||
> library1 |> toUplet >> onlyInitials
|
||||
[('A',20),('C',50),('B',25),('B',89),('D',60)] : List ( Char, Int )
|
||||
|
||||
foldForX : (TitleCount -> FinalCount -> FinalCount)
|
||||
-> FinalCount
|
||||
-> List TitleCount
|
||||
-> FinalCount
|
||||
Oh, but oh. There are two Bs in there! Those should go together and their counts
|
||||
added together, and for that, nothing is better than a fold. Let's write a function
|
||||
that answers the question: “How many X is there?” where X is a Char.
|
||||
|
||||
After a call to this, you'll get how many books have a title that starts with
|
||||
the initial X.
|
||||
howMany : Char -> List Char Int -> Int
|
||||
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 ->
|
||||
FinalCount)`, and you reflect a little bit on its name and on its usage. Hmmm,
|
||||
it takes a TitleCount and a FinalCount and returns a (new) FinalCount? You
|
||||
choose to call it:
|
||||
`howMany` is made of two functions: the first is one that only keeps tuples
|
||||
of interest (they match our Char c) and then we blindly sum the count of the
|
||||
resulting list. We always start the count from 0, so that if we don't find
|
||||
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
|
||||
that:
|
||||
> library1 |> toUplet >> onlyInitials >> howMany 'B'
|
||||
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