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 ( 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]