2

I am trying to encrypt the string "aaaabbccdeee" gets converted into "a4bbccde3"

I wasn't able to get it working in one function

Working Solution 1:

import Data.List (group)
gab0 :: [Char] -> [(Char, Int)]
gab0 = map(\l@(x:xs) -> (x, length l)) . group

gab1 :: (Char, Int) -> String
gab1 (d, 1) = [d]
gab1 (d, 2) = [d] ++ [d]
gab1 (d, x) = [d] ++ show x


gab :: [(Char, Int)] -> String
gab = foldr(\x acc ->  gab1 x ++ acc) [] 

{-
 - gab $ gab0 ['a', 'a', 'a', 'b', 'c', 'c','c','c','d','e','e']
"a3bc4de2"
-}

Solution 2 not working:

gab2 :: [Char] -> Int -> [Char]
gab2 [x] n 
    | n > 1 = [x] ++ show n
    | otherwise = [x]
gab2 (x:y:xs) n
    | x == y = gab2 (x:xs) (n + 1)
    | otherwise = getnum x :  gab2 (y:xs) 0 
    where getnum a = [a] ++ show n ++ []

"aaaabbccdeee" transformed to "a4bbccde3"

4
  • 4
    It's completely normal to use helper functions to define something Commented Jun 26, 2019 at 20:24
  • 1
    You are trying to write too much logic in a single function. It usually is better, software-design-wise to make small reusable components. Commented Jun 26, 2019 at 20:26
  • 6
    As a note totally unrelated to your question: that fits the modern usage of "encoding" more than "encrypting". Encryption suggests that it's intended to obscure the original. Encoding merely means changing the representation. It also fits with the fact that the process you're implementing usually is called "run-length encoding". Commented Jun 26, 2019 at 20:31
  • 2
    @Carl This is almost RLE, but with an "optimization" that makes it impossible to round-trip if the input string contains any numbers. You really need to convert "34" to "3141", rather than "34", if you want to handle all possible inputs. Commented Jun 26, 2019 at 21:59

2 Answers 2

3

Sounds like you are trying to compress your first definition into a single function -- i.e. you just need to shuffle some working code around. Haskell is great at this.

I would personally keep gab1 separate, since it has a couple special cases. Typically it would be written in curried form

gab1 :: Char -> Int -> String
gab1 d 1 = [d]
gab1 d 2 = [d,d]
gad1 d x = [d] ++ show x

When gab0 builds a (Char,Int) pair, the only thing it is ever used for is to be passed to gab1 later. So we can fuse that layer:

gab0 :: String -> [String]
gab0 = map (\l@(x:_) -> gab1 x (length l)) . group

Now, since we've moved the application of gab1 to gab0 (gosh these need some better names, huh?), gab has been reduced to:

gab :: [String] -> String
gab = foldr (\x acc -> x ++ acc) [] 
    = foldr (++) []
    = concat

(I'm using chained = to indicate multiple reductions in a row -- in the real code you need to pick one, but they are all equivalent).

Since gab is so simple, why don't we just do it as part of gab0?

gab0 :: String -> String
gab0 = concat . map (\l@(x:_) -> gab1 x (length l)) . group

We can move our gab1 into a where clause since it is only used for a limited scope. I'm going to rename it to renderRun, since it converts a "run" into a string.

gab0 :: String -> String
gab0 = concat . map (\l@(x:_) -> renderRun x (length l)) . group
    where
    renderRun d 1 = [d]
    renderRun d 2 = [d,d]
    renderRun d x = [d] ++ show x

Is that more to your liking?

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much
2

You are almost there, the solution is fine, sub Tasks / sub Functions are healthy to programmers eyes, so, don't worry, you can create another extra function by compose them:

gab2 :: [(Char, Int)] -> String
gab2 = foldr(\x acc ->  gab1 x ++ acc) []

finalGab :: [Char] -> String
finalGab = gab2 . gab0 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.