fpkata-relex/ep0001/Solution.md

164 lines
4.5 KiB
Markdown
Raw Normal View History

2018-01-11 08:24:25 +01:00
# 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 }