Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell string manipulation

I am trying to create a function that combines strings given a list of Integers. For example, say the function was given [1,2,3], then the output would be " ***". Basically, each number represents a * with spaces before it. So the number 5 would be " *" which is 4 spaces followed by the *. However, I am given a list and I can't just ++ them all together because the strings mess up the order.

My idea was to start out by taking the first element in the list and send it back as the string recursively. So for [1,2,3] I would send back to the function [2,3] with String = " *". Next, for each element I check if the length of the string -1 is <= the next element (- 1 because 0 is included). In the list that I am giving the function, this will ALWAYS be the case (the list will always be a number from 0-9, increasing, and NO repeats). Then I would ++ the original string to the function call again to make the recursive statement. I did this till there was nothing left. Here is what I've done:

makeStr :: [Integer] -> String -> String
makeStr [] _ = ""
makeStr (x:xs) s
        | null xs && s == ""               = getStar ((fromIntegral x)) "*"
        | null xs && s /= ""               = s ++ getStar ((fromIntegral x) - (length s)) "*"
        | s == ""                          = makeStr xs (getStar ((fromIntegral x) - ((length s))) "*")
        | length s - 1 <= (fromIntegral x) = s ++ makeStr xs (getStar ((fromIntegral x) - ((length s))) "*")

Note: getStar is a simple function that takes a number and "*" and returns the string with the correct amount of spaces. It has the declaration of getStar :: Int -> String -> String. This function works perfectly, I have tested it numerous times and I really do not believe it is the issue, hence why I did not include it.

An example of getStar function is:

getStar 3 "*"
"   *"

some example inputs with expected outputs:

makeStr [1,2,3] ""
" ***"

makeStr [0,2,3] ""
"*  **"

makeStr [1,2,3,4] ""
" ****"

makeStr [0,9] ""
"*        *"

The output of my program is incorrect for any list over 2 elements.

makeStr [0,1] "" -- correct
"**"

makeStr [1,2,3] "" -- incorrect, should be " ***"
" **  *"

makeStr [1,2,3,4] "" -- incorrect, should be " ****"
" **  * *"

I can't figure out why it is correct for the first 2 elements, then incorrect for anything after. I've traced through it multiple times but it all seems like it should work fine.

Edit Solution:

makeStr :: [Integer] -> String
makeStr [] = ""
makeStr (x:xs)
            | x == 0 = "*" ++ makeStr (map (subtract 1) xs)
            | x /= 0 = " " ++ makeStr (map (subtract 1) (x:xs))
like image 802
William Jones Avatar asked Mar 04 '23 05:03

William Jones


2 Answers

A possible solution could follow these steps.

  • If the input list is empty, return the empty string.
  • If the input list is x:xs, check x.
    • If x==0, then emit a '*', and recurse with xs where each number has been decremented
    • If x/=0, then emit a ' ', and recurse with x:xs where each number has been decremented

E.g.

f [1,3]
= ' ' : f [0,2]
= ' ' : '*' : f [1]
= ' ' : '*' : ' ' : f [0]
= ' ' : '*' : ' ' : '*' : f []
= ' ' : '*' : ' ' : '*' : []
= " * *"

The above is not efficient as it could be. One can improve the efficiency keeping a second "offset" integer argument, and incrementing that instead of decrementing the whole list.

like image 96
chi Avatar answered Mar 15 '23 13:03

chi


@chi solution, but without the need to decrement every element of the list at every recursive step.

makeStr = f 0

f _ [] = ""
f i (x:xs)
    | x - i == 0 = '*' : f (i+1) xs
    | otherwise  = ' ' : f (i+1) (x:xs)
like image 37
Fábio Roberto Teodoro Avatar answered Mar 15 '23 12:03

Fábio Roberto Teodoro