164 lines
4.5 KiB
Markdown
164 lines
4.5 KiB
Markdown
|
# FPKATA #01: In the library…
|
||
|
|
||
|
(Soundtrack: Arch Enemy — War Eternal)
|
||
|
|
||
|
(Inspired from this exercice: http://www.codewars.com/kata/54dc6f5a224c26032800005c/train/haskell)
|
||
|
|
||
|
## Introduction
|
||
|
|
||
|
You are stuck in a library with a very talkative and experimental science-minded IA. It keeps repeating the following:
|
||
|
|
||
|
> — Pleeease, can you help me?
|
||
|
|
||
|
> — …
|
||
|
|
||
|
> — Pleaaase, can you help?
|
||
|
|
||
|
> — …
|
||
|
|
||
|
> — Hellooooo?
|
||
|
|
||
|
> — …
|
||
|
|
||
|
Since you would really like to have peace, you dare ask:
|
||
|
|
||
|
> — What's the matter?
|
||
|
|
||
|
> — My code is incomplete, I've lost my code, please help!
|
||
|
|
||
|
> — 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…
|
||
|
|
||
|
> — Wait!
|
||
|
|
||
|
> — … of characters that my master will provide.
|
||
|
|
||
|
> 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:
|
||
|
|
||
|
INPUT................:
|
||
|
LIBRARY COUNT......: [("ABOOK", 12), ("BPDEXU", 45), ("BJLDRSA", 15), ("CLDAVB", 98), ("DAJLDI", 32)]
|
||
|
INDICES............: ['A','B','C','W']
|
||
|
OUTPUT...............:
|
||
|
INDEX COUNT........: [('A',12),('B',60),('C',98),('W',0)]
|
||
|
TIME TO GOOD ANSWER..: 203 years
|
||
|
|
||
|
You frown:
|
||
|
|
||
|
> — It took you so long to find this?
|
||
|
|
||
|
> — Can you help me? I've lost my code, my dedicated code for this task.
|
||
|
|
||
|
> — Well, you managed to get this one right, didn't you?
|
||
|
|
||
|
> — Nooooo! Master is going to unplug me if I use bruteforce again! Master was
|
||
|
very unhappy with my performance! I want to live! Please help me!
|
||
|
|
||
|
> — Ok, all right, calmn down, there, there… What are programmed with?
|
||
|
|
||
|
> — Elm.
|
||
|
|
||
|
Of course it's Elm…
|
||
|
|
||
|
## Outline
|
||
|
|
||
|
```
|
||
|
module Main exposing (..)
|
||
|
|
||
|
import Html exposing (text)
|
||
|
|
||
|
|
||
|
type Stock
|
||
|
= Stock String Int
|
||
|
|
||
|
|
||
|
library1 : List Stock
|
||
|
library1 =
|
||
|
[ Stock "ABART" 20
|
||
|
, Stock "CDXEF" 50
|
||
|
, Stock "BKWRK" 25
|
||
|
, Stock "BTSQZ" 89
|
||
|
, Stock "DRTYM" 60
|
||
|
]
|
||
|
|
||
|
|
||
|
categories1 : List Char
|
||
|
categories1 =
|
||
|
[ 'A'
|
||
|
, 'B'
|
||
|
, 'C'
|
||
|
, 'W'
|
||
|
]
|
||
|
|
||
|
|
||
|
stockList : List Stock -> List Char -> List ( Char, Int )
|
||
|
stockList st cs =
|
||
|
<THIS IS BLANK, FILL ME>
|
||
|
|
||
|
|
||
|
main =
|
||
|
text (toString (stockList library1 categories1))
|
||
|
```
|
||
|
|
||
|
## 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:
|
||
|
|
||
|
foldl : (a -> b -> b) -> b -> List a -> b
|
||
|
|
||
|
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.
|
||
|
|
||
|
### Inner foldl: how many books with Initial X
|
||
|
|
||
|
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.
|
||
|
|
||
|
type alias Initial = Char
|
||
|
type alias Count = Int
|
||
|
type alias TitleCount = (Initial, Int)
|
||
|
type alias FinalCount = (Initial, Int)
|
||
|
|
||
|
You rewrite your specialized foldl type signature to account for the type
|
||
|
aliases:
|
||
|
|
||
|
foldForX : (TitleCount -> FinalCount -> FinalCount)
|
||
|
-> FinalCount
|
||
|
-> List TitleCount
|
||
|
-> FinalCount
|
||
|
|
||
|
After a call to this, you'll get how many books have a title that starts with
|
||
|
the initial X.
|
||
|
|
||
|
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:
|
||
|
|
||
|
updateFinalCount : (TitleCount -> FinalCount -> FinalCount)
|
||
|
|
||
|
Actually it takes another (hidden) parameter, you change its name to reflect
|
||
|
that:
|
||
|
|
||
|
updateFinalCountForX : (TitleCount -> FinalCount -> FinalCount)
|
||
|
updateFinalCountForX t f =
|
||
|
|
||
|
|
||
|
NB:
|
||
|
addBio : String -> User -> User
|
||
|
addBio bio user =
|
||
|
{ user | bio = bio }
|