Question

I'm attempting problem 11 of "99 Haskell Problems." The problem description is pretty much:

Write a function encodeModified that groups consecutive equal elements, then counts each group, and separates singles from runs.

For example:

Prelude> encodeModified "aaaabccaadeeee"
[Multiple 4 'a',Single 'b',Multiple 2 'c',
 Multiple 2 'a',Single 'd',Multiple 4 'e']

Here's my working code:

module Batch2 where

import Data.List -- for `group`
data MultiElement a = Single a | Multiple Int a deriving (Show)

encodeModified :: (Eq a) => [a] -> [MultiElement a]
encodeModified = map f . group
  where f xs = case length xs of 1 -> Single (head xs)
                                 _ -> Multiple (length xs) (head xs)

I'd like to take out that pesky repeated (head xs) in the final two lines. I figured I could do so by treating the result of the case clause as a partially applied data constructor, as follows, but no luck:

encodeModified :: (Eq a) => [a] -> [MultiElement a]
encodeModified = map f . group
  where f xs = case length xs of 1 -> Single
                                 _ -> Multiple length xs 
               (head xs)

I also tried putting parenthese around the case clause itself, but to no avail. In that case, the case clause itself failed to compile (throwing an error upon hitting the _ symbol on the second line of the clause).

EDIT: this error was because I added a parenthesis but didn't add an extra space to the next line to make the indentation match. Thanks, raymonad.

I can also solve it like this, but it seems a little messy:

encodeModified :: (Eq a) => [a] -> [MultiElement a]
encodeModified = map (\x -> f x (head x)) . group
  where f xs = case length xs of 1 -> Single
                                 _ -> Multiple (length xs)

How can I do this?

Was it helpful?

Solution

The function application operator $ can be used to make this work:

encodeModified = map f . group
  where f xs = case length xs of 1 -> Single
                                 _ -> Multiple (length xs)
               $ head xs

OTHER TIPS

You could match on xs itself instead:

encodeModified :: (Eq a) => [a] -> [MultiElement a]
encodeModified = map f . group
  where f xs = case xs of (x:[]) -> Single x
                          (x:_)  -> Multiple (length xs) x

or more tersely as

encodeModified :: (Eq a) => [a] -> [MultiElement a]
encodeModified = map f . group
  where f (x:[]) = Single x
        f xs@(x:_)  = Multiple (length xs) x

or even

encodeModified :: (Eq a) => [a] -> [MultiElement a]
encodeModified = map f . group
  where f as@(x:xs) = case xs of [] -> Single x
                                 _  -> Multiple (length as) x

Admittedly most of these have some repetition, but not of function application.

You could also go with let:

encodeModified :: (Eq a) => [a] -> [MultiElement a]
encodeModified = map f . group
  where f xs = let x   = head xs 
                   len = length xs in 
        case len of 1 -> Single x
                    _ -> Multiple len x
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top