summaryrefslogtreecommitdiff
path: root/lec03/Golf.hs
blob: f4b988b72325ef709b651d52442562452abd793f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
module Golf where
import Data.List
import Data.Maybe

-- Exercise 1

-- Explanation:
-- takeNth takes the nth value of a list without crashing. First, it drops all
-- the first `n - 1` elements from the list. Then, it takes the desired element
-- and returns it as a list. If the element can't be found, it returns an empty
-- list. This function is total and can be thought of as the total version of
-- the partial function `!!`.
takeNth :: [a] -> Int -> [a]
takeNth l n = take 1 $ drop (n-1) l

-- Explanation:
-- skips first maps all values in `nl`, defined as all values from 1 to the
-- length of the list (excluded), to `concatMap`, which is the same as `concat
-- (map ...)`. `concatMap` maps again the same array, this time multiplied by
-- each value `n` coming from the outer map, to takeNth of the given list `l`
-- and concatenates the result.
skips :: [a] -> [[a]]
skips [] = []
skips l = map (\n -> concatMap (takeNth l . (*n)) nl) nl
    where
        len = length l
        nl = [1..len]

-- Exercise 2

-- Explanation:
-- localMaxima is defined in two cases. In the first case, the argument of the
-- function is a list with at least 3 elments. Depending on the value of the
-- first three elements, checked through two guards, the second is either
-- included or excluded from the resulting list. In particular, the first guard
-- checks whether both the first and the third are smaller than the second using
-- the `all` function. The second case matches if the list has less than 3
-- elements, in which case the result is just an empty list.
-- The list resulting from the `localMaxima` function is built recursively.
localMaxima :: [Integer] -> [Integer]
localMaxima (x1:x2:x3:xs)
    | all (<x2) [x1,x3] = x2 : localMaxima (x2:x3:xs)
    | otherwise = localMaxima (x2:x3:xs)
localMaxima l = []

-- Exercise 3

-- Explanation:
-- `Occs` is a type alias for a list of occurrences.
type Occs a = [(a, Int)]

-- Explanation:
-- `bar` takes two arguments and returns a string that represents a bar from a
-- bar chart. The first argument is the maximum space available for the bar
-- while the second is the bar's length. The function uses two `replicate` calls
-- to first draw the bar and then add some padding in the unused space. If the
-- bar length exceeds the available space, the bar is cut using `drop`, which
-- removes the length in excess.
bar :: Int -> Int -> String
bar max n
    | max >= n = replicate n '*' ++ replicate (max-n) ' '
    | otherwise = drop (n-max) $ replicate n '*'

-- Explanation:
-- `occ` takes a list of occurrences and a single occurring element. With them,
-- it returns the amount of occurrences the element has, or 0 if the element
-- does not occur.
-- I chose to keep this function separate from the main `histogram` function for
-- the sake of readability. I found that histogram is already complex enough to
-- add anything else to it.
occ :: Eq a => Occs a -> a -> Int
occ o e = fromMaybe 0 $ lookup e o

-- Explanation:
-- `histogram` takes a list of `Integer`s and returns a string that represents a
-- bar chart with the number of occurrences for each value between 0 and 9. The
-- function operates as follows. First, it draw the bars in horizontal style,
-- from left to right. Bars are drawn as the local variable `bars` says: it
-- draws bars for all values from 0 to 9 by mapping those values to the bar
-- function, with the maximum space being the maximum of all the second elements
-- of the occurrence pairs (see `Occs`) and the length being the length of the
-- current value (see `occ`). After drawing the bars, they are transposed, so
-- that they are shown as columns, and a footer is inserted at the beginning.
-- The footer comes first because the chart is then completely reversed (the
-- last row becomes the first, etc.) and the lines are joined using `unlines`.
histogram :: [Integer] -> String
histogram l = unlines $ reverse $ ["0123456789", "=========="] ++ transpose bars
    where
        count = map (\e -> (head e, length e)) $ group $ sort l
        bars = map (bar (maximum $ map snd count) . occ count) [0..9]