Search code examples
stringsplitfunctional-programmingclean-language

Split string to a list of strings in Clean


Because of the limited amount of resources, I need to propose a question here. I have been struggling with functional programming, the endless Haskell tutorials don't really help me. So what I want to achieve, in Clean language, is to split a string like " car cow cat " to a list of strings ["car","cow","cat"]. Can you provide me a detailed answer (does not have to be complete code), on how to iterate through this string, and especially the part when the newly constructed strings are added to the list?


Solution

  • I'm going to offer a simple solution. There are infinitely better ways of doing this in Haskell, but it's the simplest I can think for someone new in functional programming, without using any specifically Haskell function like takeWhile, or even any folds and maps...

    You basically want to simulate iterating over a list, so here is what I suggest:

    1. Define a function that will take a string and a split-by character. This function will return a list of strings - spliton :: String -> Char -> [String]

    2. To move over the list, we'll want to gobble up characters until we hit one of our splitting characters. We'll also want to save the word we've saved up until now, and the entire list of words. For that, we'll define a subfunction that will save the states

      spliton' :: String -> Char -> String -> [String] -> [String]

      spliton' [] _ sofar res = res ++ [sofar]

      I've also included the simplest clause - an empty string. When our string is empty, we'll just want to return what we have saved so far.

    3. Now lets move on to our actual recursive function: If we hit our split character, we'll add the string we have saved so far to the list and restart with an empty current-state string If we don't hit the split character, we'll add the character to the current-state string

      spliton' (currchar:rest) splitby sofar res
           | currchar==splitby = spliton' rest splitby "" (res++[sofar])
           | otherwise = spliton' rest splitby (sofar++[currchar]) res
      

    So, to summarize our code:

    spliton :: String -> Char -> [String]
    spliton source splitchar = spliton' source splitchar [] []
    
    spliton' :: String -> Char -> String -> [String] -> [String]
    spliton' [] _ sofar res = res ++ [sofar]
    spliton' (currchar:rest) splitby sofar res
             | currchar==splitby = spliton' rest splitby "" (res++[sofar])
             | otherwise = spliton' rest splitby (sofar++[currchar]) res
    

    Note: This will not however get rid of the empty string - meaning if you have many superfluous spaces - you'll get them added to the list. I'll leave you to think how to handle that case - hope this can help you get started.