Search code examples
stringhaskellinputstatefold

Using states when traversing through a String/Text in Haskell


I am quite a beginner in Haskell and I am having an issue solving a problem, which is the following:
I am getting a file as an input (Text/String type, performance is not an important parameter now), and I am trying to format it to drop certain parts of it.
I was thinking that I would fold it and put the contents of the file into a String/Text, and whenever I reach the parts I want to drop (as it is indicated with a symbol when the droppable contents are coming and it has a symbol when valid content is coming), I change the "state" of the fold that now it shouldn't accumulate and when the symbol which indicates the coming of the real content comes back, the state should tell the fold to continue the accumulation.
What I've tried until now is the following:

checkForComment :: String -> Bool -> Bool
checkForComment "/*" _ = False
checkForComment "*/" _ = True
checkForComment "//" _ = False 
checkForComment "/n" _ = True 
checkForComment _ True = True
checkForComment _ False = False 

eraseComments :: [String] -> String
eraseComments = foldr (\emptyList listWithInput -> if checkForComment listWithInput then listWithInput ++ emptyList else []) []

This current example is not working of course, because for my checkForComment function, I should pass a parameter, which describes the previous state.
I hope I properly described the task, and unfortunately I am not able to solve it. If anyone can help, I would be very happy! :D


Solution

  • The most basic option is to use explicit recursion, writing a helper function that takes a flag meaning "am I inside a comment?". For instance

    removeC :: String -> Bool -> String
    removeC ('/':'*':rest) False = removeC rest True
    removeC (x:rest)       False = x : removeC rest False
    removeC ('*':'/':rest) True  = removeC rest False
    removeC (_:rest)       True  = removeC rest True
    removeC ""             _     = ""
    

    Then, you define the actual function providing the initial state for the flag:

    eraseComments :: String -> String
    eraseComments str = removeC str False
    

    One can turn this into a fold, but that might not lead to simpler, more readable code since you need to handle the extra flag.

    Mind that the above code is only an example, and probably does not fit your task exactly. Still, you probably get the general idea, and can try to adapt this to fit your needs.