简体   繁体   中英

Act on a `case` clause in Haskell

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?

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM