UPenn CIS 194 Homework 3: Code golf I am working through UPenn CIS 194: Introduction to Haskell (Spring 2013). Since I am not able to take the course for real I am asking for CR (feedback) as it could be from teacher in that course.

HW3 - Code golf - Full description

module Golf where

import Data.List

-- ["apple","orange","plum"] example
-- ... [("apple",0),("orange",1),("plum",2)] added indexes
-- ... [("apple",0),("orange",1),("plum",2)] filtered by predicate: index `mod` 1 == 0
-- [["apple","orange","plum"]] ++ ... [("orange",0),("plum",1)] added indexes
-- [["apple","orange","plum"]] ++ ... [("orange",0)] filtered by predicate: index `mod` 2 == 0
-- [["apple","orange","plum"]] ++ [["orange"]] ++ ... [[("plum",0)]] added index
-- [["apple","orange","plum"]] ++ [["orange"]] ++ ... [[("plum",0)]] filtered by predicate: 0 `mod` 3 == 0 
-- [["apple","orange","plum"]] ++ [["orange"]] ++ [["plum"]]
-- [["apple","orange","plum"],["orange"],["plum"]]
skips :: [a] -&gt; [[a]]
skips = skips' 1

skips' :: Integer -&gt; [a] -&gt; [[a]]
skips' _ [] = []
skips' n root@(_:xs) = [fst $ unzip $ filter devidedByIndex $ zip root [0..]] ++ skips' (n + 1) xs
  where
    devidedByIndex x = (snd x) `mod` n == 0

-- [1,2,9,3]
-- 2 &gt; 1 &amp;&amp; 2 &gt; 9 = False
-- localMaxima [2,9,3]
-- 9 &gt; 2 &amp;&amp; 9 &gt; 3 = [9] ++ localMaxima [3]
-- [9] ++ []
-- [9]
localMaxima :: [Integer] -&gt; [Integer]
localMaxima [] = []
localMaxima (x:[]) = []
localMaxima (x:y:[]) = []
localMaxima (x:y:z:xz)
  | y &gt; x &amp;&amp; y &gt; z = [y] ++ localMaxima (z:xz)
  | otherwise      = localMaxima (y:z:xz)

-- putStr (histogram [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5,6,6,6,6,7,7,7,8,8,9])
--      *
--     ***
--    *****
--   *******
--  *********
-- ==========
-- 0123456789
histogram :: [Integer] -&gt; String
histogram m = transform (maximum n) n
  where
    n = countElements m

transform :: Int -&gt; [Int] -&gt; String
transform 0 _  = "==========\n0123456789\n"
transform i n  = intercalate "" ((map transform' n) ++ ["\n"]) ++ transform (i - 1) n
  where
    transform' x
      | x &gt;= i    = "*"
      | otherwise = " "

-- count how many times each number appears in array
countElements :: [Integer] -&gt; [Int]
countElements n = map count [0..9]
  where
    count x = length (elemIndices x n)
go

First just a blanket statement about names. Since this is
a golfing challenge you should pick short names for
varibles, helper functions, etc. Of course, that makes
things less readable, but there are some conventions that
Haskellers follow:

go         - names a local helper function
xs, ys, zs - a list
(x:xs)     - xs is the tail of a list whose head is x
x'         - a value related to x


Some people complain about the heavy use of single letter
variable names in Haskell, but when used judiciously I think
it actually aids readability.

skips

You can perform filter, fst and unzip all in a single
list comprehension like this:

skips' n root@(_:xs) = [ x | (x,k) &lt;- zip root [0..], mod k n == 0] ++ ...


Also, note that you define devidedByIndex, but you only call it once.
In a situation like that you can always inline the definition where it is used,
and that's what we've done here by putting the mod k n ... expression
directly into the list comprehension.

localMaximum

You have three cases which return the same value, so just put the
last case first and use a default pattern for the others:

localMaxima (x:y:z:xz) = ...
localMaximum _ = []


Next, because we're golfing, you can avoid the otherwise clause and
duplicating the recursion call like this:

localMaxima (x:y:z:zs) = (if x &lt; y &amp;&amp; y &gt; z then [y] else []) ++ localMaximum ...


histogram

There are two sub-problems:


Computing the counts
Building the picture.


Let's start with #2, and say we have the counts in a list ns, i.e. the counts
for the example is [0,1,2,3,4,5,4,3,2].

A quick way to create the row for the k-th level is:

row k ns = [ if n &gt;= k then '*' else ' ' | n &lt;- ns ]


and then we just need to stack those lines together:

(row 9 ns) ++ "\n" ++ (row 8 ns) ++ "\n" ++ ... (row 1 ns) ++ "\n"


Perhaps this gives you some ideas on how to improve the code.

In your computeElements, again note that your helper count is only used once,
so for golfing purposes you can inline it:

countElements n = map (\x -&gt; length (elemIndices x n)) [0..9]


Also, compare:

elementIndices x n          - 16 non-space chars
[ y | y &lt;- n, y == x ]      - 13 non-space chars


Since you only interested in the length both will work.

Finally, whenever you have map (\x -&gt; ...) [...] it's the same as
a list comprehension:

map (\x -&gt; .A.) [.B.]  = [ .A. | x &lt;- [.B.]]


and the list comprehension is shorter by a few characters.

import ...

The exercise encourages you to look for standard library functions to help
you reduce your code size.

Here are some library functions which might be applicable to these problems:

tails :: [a] -&gt; [[a]]
sort :: Ord a =&gt; [a] -&gt; [a]
group :: Eq a =&gt; [a] -&gt; [[a]]
unlines :: [String] -&gt; String