3

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))
4
  • Can you write a function mergeStars :: String -> String -> String that takes two of these star-lists and produces one that combines stars from both? Hint: you might want to use zipWith. Commented Apr 6, 2019 at 7:56
  • I would find the maximum of the list, and then map over the enumeration [0..maxOfList] with a function that checks if the number is in the input list, and produces '*' or ' ' accordingly. Commented Apr 6, 2019 at 8:02
  • what's the output for [2,2]? and for [3,1]? Commented Apr 6, 2019 at 9:34
  • Looks like the s in makeStr (x:xs) s is never used. The type signature could not be just makeStr :: [Integer] -> String instead of makeStr :: [Integer] -> String -> String? Commented Apr 6, 2019 at 11:43

2 Answers 2

4

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.

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

4 Comments

You have to check every element at every step, consider f [3, 1].
@Koterpillar The OP stated that "the list will always be a number from 0-9, increasing, and NO repeats", so that can not happen.
Sorry, I didn't notice, was just trying to reverse engineer the requirements from the examples :)
Ahh, I wish I would have thought of this. I really over complicated what I had to do haha, I'll update my post with this solution
2

@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)

1 Comment

I prefer f i (x:xs) = replicate (x-i) " " ++ '*' : f (x+1) xs, no need to single-step the integer accumulator

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.