Eric Banderhide
Eric Banderhide

Reputation: 31

Getting started with strings in Haskell

I need to write a function that takes a string of gggggggeeeeetttttt and can count how many times the letter repeats and output as g7e5t6.

I've only just started Haskell so not really sure at all where to start.

Upvotes: 3

Views: 1424

Answers (4)

Vagif Verdi
Vagif Verdi

Reputation: 4896

Abuse of arrows and pointfree :)

f = group >>> concatMap (head &&& (show . length) >>> uncurry (:))

And here's with applicative:

f = concatMap ((:) <$> head <*> (show . length)) . group

Upvotes: 1

chrisdb
chrisdb

Reputation: 1115

The function group will group together identical elements in a list. Since a string is a list of characters, we have:

group "ggggeeetttt" = ["gggg","eee","tttt"]

To get the letter, we can use map head, since head takes the first element of a string (or any list):

map head ["gggg","eee","tttt"] = ['g','e','t']

Now, we want to know how many elements there are in each substring. This can be done using map length:

map length ["gggg","eee","tttt"] = [4,3,4]

Let's turn these numbers back into strings:

map show [4,3,4] = ["4","3","4"]

Now we need to combine the original list and length list somehow. We can do this as follows:

zipWith (:) ['g','e','t'] ["4","3","4"]

zipWith will apply the specified function to paired elements in the two lists. Here I've used (:), which adds an element to the start of the list.

This gives you all the building blocks you need to do what you want. For example, this should work, although I haven't tested it:

f s = concat $ zipWith (:) letters lengths
    where
        groups  = group s
        letters = map head groups
        lengths = map (show . length) groups

Upvotes: 13

gorlum0
gorlum0

Reputation: 1455

Variant with comprehensions, imo a bit prettier (considering previous explanations just code):

ghci> let s = "gggggggeeeeetttttt"
ghci> putStrLn $ concat [head g: show (length g) | g <- group s]
g7e5t6

Upvotes: 7

fuz
fuz

Reputation: 93034

The best way to start, is to figure out your problem. I would suggest the following method:

  1. Split the string into substrings of equal characters. So, you need a function which takes a String and returns, a list of strings ([String]).
    Example: splitStr "gggggggeeeeetttttt" should return ["ggggggg","eeeee","tttttt"]
    Happily, the module Data.List already provides such a function, it's named group.
  2. Calculate the number of repetitions and the letter which is repeated. The number of repetition is simply the length of the sublist, use length for that.
  3. Get the repeated character and stick it in front of the number of repetitions. For the repeated character, we may use any one of the list, we take the first one. To calculate the first element of a list (aka string), you can use head. Eg. head "abc" yields 'a'.
    To stick this in front of the length, you can use a small helper function and show, which turns most stuff into a string. Our helper looks like this: makeRepetitions string = head string : show (length string). : aka cons adds an element to the front of a list.
  4. Apply the helper of step (3) to all elements of the list of string in (1). We use map for that. map applies a function to a list of values and returns the list of results
  5. concat the sublists together to your result. We can use concat for this. Actually, we can combine (4) and (5) using concatMap. This function maps a function to a list of values and concats the results - just as we want it.

Now, your code looks like this:

import Data.List

runlength :: String -> String
runlength string = concatMap makeRepetitions (group string)) where
  makeRepetitions string = head string : show (length string)

Because that much brackets are annoying, Haskellers often use ..The dot combines two functions to create a new one. You can think of f = functionA . functionB is equal to f x = functionA (functionB x). Using the dot, we can reformat the program a bit:

import Data.List

runlength :: String -> String
runlength = concatMap makeRepetitions . group where
  makeRepetitions string = head string : show (length string)

IMO, This representation is more readable. You can see runlength as a pipeline. First apply group, then we use concatMap to map makeRepetitions onto the input and concat the results.

Upvotes: 3

Related Questions