开发者

Shortening a String Haskell

How do you shorten a string in Haskell with a given number.

Say:

comp :: String -> String
short :: String -> String

chomp (x:xs) = (x : takeWhile (==x) xs)

using comp I want to select a run of repeated characters from the start of a string, with the run comprising at most nine characters.

For example:

short "aaaavvvdd" would output "aaaa" and short "dddddddddd" outputs "ddddddddd".

I know I need take but am not sure how to put that into the code.

i've got th开发者_运维问答is far but it doesn't work

short x:xs | length(short x:xs) >9 = take(9)
           | otherwise = comp


The Quick Answer

import Data.List
short [] = []
short x = (take 9 . head . group) x  

This will give you output that matches your desired output.

That is,

*> short "aaaavvvdd"
"aaaa"
*> short "dddddddddd"
"ddddddddd"

Step by Step Development

Use "group" to separate the items

This solution depends on the "group" function in the Data.List library. We begin with the definition:

short x = group x

This gives us:

*> short "aaaavvvddd"
["aaaa","vvv","ddd"]

Use "head" to return only the first element

Once we have the the elements in a list, we want only the first item of the list. We achieve this using "head":

short x = (head . group) x

"." is the Haskell function for function composition. It's the same as:

short x = head (group x)

or

short x = head $ group x

This will give us:

*> short "aaaavvvdd"
"aaaa"
*> short "dddddddddddddd"
"dddddddddddddd"

Use "take" to get the first nine characters

We finish the program by taking only the first nine characters of this result, and end up with our final function. To do this, we use the "take" function from the prelude:

short x = (take 9 . head . group) x

We now have the result that we wanted, but with one minor problem.

Add another case to eliminate the error

Note that using our current definition on the empty list causes an error,

*> short "aaaavvvddd"
"aaaa"
*> short ""
"*** Exception: Prelude.head: empty list

Because "head" is undefined on the empty list, we need to handle another case: the empty list. Now we have:

short [] = []
short x = (take 9 . head . group) x  

This is our "final answer".


Here is another version:

short xs = take 9 $ takeWhile (== head xs) xs

So we take from the list as long as the content equals the head of list (which is the first char of the string). Then we use take to shorten the result when necessary.

Note that we don't need an additional case for empty strings, which is a consequence from Haskell's lazyness: If takeWhile sees that the list argument is empty, it doesn't bother to evaluate the condition argument, so head xs doesn't throw an error.


Here's a definition:

import Data.List (group)
short = take 9 . head . group

Interestingly enough, since our returned string is a prefix of the original string, and is constrained to be at most 9 characters long, it doesn't matter whether we trim down to that limit first or last. So we could also use this definition:

short = head . group . take 9

Both of these are written in the "pointfree" style which doesn't reference a lack of punctuation, but a lack of unnecessary variables. We could have also written the definition as

short s = take 9 (head (group s))

Or, using $ to get rid of parentheses:

short s = take 9 $ head $ group s

The only other step is to extract only the first block of matching characters, which is what head . group does (equivalent to your chomp function).

From the docs:

group :: Eq a => [a] -> [[a]]

The group function takes a list and returns a list of lists such that the concatenation of the result is equal to the argument. Moreover, each sublist in the result contains only equal elements. For example,

group "Mississippi" = ["M","i","ss","i","ss","i","pp","i"]

It is a special case of groupBy, which allows the programmer to supply their own equality test.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜