Andrew
Andrew

Reputation: 1025

Foldl on string

I would like to foldl a string so that any occurrence of zero which is preceded by @ is replaced with "k". So "[email protected]" becomes, "[email protected]". How do I do that? So far my attempt is

test = foldl(\x acc-> if((last acc) == "@" && x == "0" then acc ++ "k" else acc ++ x)) "" "[email protected]"

However, it doesn't work. Foldl is expecting list of string and what I'm providing is just a string. How do I overcome that?

Upvotes: 3

Views: 2056

Answers (2)

dfeuer
dfeuer

Reputation: 48580

Taking chi's advice,

rep "" = ""
rep ('@' : '0' : xs) = "@k" ++ rep xs
rep (x : xs) = x : rep xs

If we want to get fancier,

rep = snd . mapAccumL go False
  where
    go True '0' = (False, 'k')
    go _ '@' = (True, '@')
    go _ x = (False, x)

or even

rep = snd . mapAccumL go 'x'
  where
    go '@' '0' = ('0', 'k')
    go _ y = (y, y)

To use foldr with this second approach (just because it's shorter; the first will work fine too, and allows generalization),

rep xs = foldr go (const "") xs 'x'
  where
    go '0' r '@' = 'k' : r '0'
    go x r _ = x : r x

To use zipWith (which is more awkward to generalize):

rep xs = zipWith go ('x' : xs) xs where
  go '@' '0' = 'k'
  go _ x = x

Upvotes: 8

behzad.nouri
behzad.nouri

Reputation: 77941

As others have commented, foldl would not fit well for this task. However, using the idea of difference list, you may still do this efficiently using foldl:

repl :: String -> String
repl a = foldl loop id a ""
    where
    loop :: (String -> String) -> Char -> (String -> String)
    loop f '@' ('0':rest) = f ('@':'k':rest)
    loop f x xs = f (x:xs)

Just for the purpose of demonstration and the fact that the question has asked for a foldl solution. (not that I would recommend doing it this way).

Upvotes: 2

Related Questions