3
\$\begingroup\$

I'm solving "mastermind game" problem from CIS haskell course. Here is the link to assignment pdf if someone is interested.

List of possible colors is defined like this:

data Peg = Red | Green | Blue | Yellow | Orange | Purple
     deriving (Show, Eq, Ord)
type Code = [Peg]
colors :: [Peg]
colors = [Red, Green, Blue, Yellow, Orange, Purple]

And I had to write a function that will take a list with color guesses and return the occurrence count for each available color in the form of a list.

countColors :: Code -> [Int]
countColors a        = map (\(x,y) -> length $ filter (x==) y) combo
      where combo    = zip colors (take 6 $ repeat a)

It works fine but I'd like to know if there is better/more expressive way to write it (using most basic available functions).

I'm not sure what's good practice in Haskell; that is my main problem. I have tried to write in point free notation at first but it looked overly complicated.

My worry is that code is not clear enough. I like to have code that is as obvious as possible.

Here are test cases in case it helps:

countColors [Red, Blue, Yellow, Purple] == [1, 0, 1, 1, 0, 1]
countColors [Green, Blue, Green, Orange] == [0, 2, 1, 0, 1, 0]
\$\endgroup\$
  • \$\begingroup\$ lol, we should call this site "grammar review" @Jamal ;D \$\endgroup\$ – Reygoch May 3 '15 at 19:55
  • \$\begingroup\$ All SE sites like posts to be well formatted etc. It doesn't take long to just check through your own question and make sure it is formatted well. It is also more likely to get answers if it makes grammatical sense, and is presented well. \$\endgroup\$ – Tim May 3 '15 at 20:36
1
\$\begingroup\$

You could map each color to the number of times it occurs in the given list, i.e.:

countColors :: [Peg] -> [Int]
countColors xs = map (\c -> length (filter (== c) xs)) colors
\$\endgroup\$
4
\$\begingroup\$

I think you're tripping yourself up by position-encoding the colors. Position-encoding is usually a code smell in Haskell, what you probably want is an association. Consider the difference between [Int] and [(Peg, Int)], and just how much more expressive of intent the latter is.

(You could be even more specific at the type level with something like type Count = Int countColors :: Code -> Map Peg Count but that's probably excessive for a small game.)

So then how do you write a function with type Code -> [(Peg, Int)]? As you noticed the [Peg] may be in any order, so to compensate you can filter for each individual color, or you could leverage the Ord Peg instance and sort the original list to group identical colors together. After the colors are in order, what's left is to simply run-length encode the list.

countColors :: Code -> [(Peg, Int)]
countColors = map (\shade -> (head shade, length shade)) . group . sort
\$\endgroup\$
  • 1
    \$\begingroup\$ A nice way to shorten this is to use the (&&&) function from Control.Arrow. It lets you write map (head &&& length) . group . sort. \$\endgroup\$ – Frerich Raabe May 4 '15 at 8:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.