MrD
MrD

Reputation: 5086

Substitue String in Haskell

Evening,

This is my attempt at an equivalent of "str_replace" in Haskell

strReplace :: (Char, Char) -> String -> String -> String {- Original (y) Parsed (z) -}
strReplace _ "" y = y
strReplace x y z = if (y !! 0) == fst x then strReplace x (drop 1 y) (z:([snd x])) else        strReplace x (drop 1 y) (z:(y!!0))

Essentially, the first Tuple is the char to be substitued (Ie ('A', 'B') replaces all As to Bs, the second parameter is the String to be parsed and the third parameter should always be left an empty string. Compiler returns

*** Expression     : z : [snd x]
*** Term           : z
*** Type           : [Char]
*** Does not match : Char

Ideas? :)

Upvotes: 0

Views: 172

Answers (2)

itsbruce
itsbruce

Reputation: 4843

z is of type [Char]. You can't use : to cons a [Char] into a [Char] - look at the type signature for :. You would have to use ++ to append one [Char] to another.

Additional points:

  1. It would be better style for strReplace to have a signature :: Char -> Char -> String -> String -> String.
  2. It would be even better style for the signature to be :: a -> a -> [a] -> [a] -> [a]
  3. You shouldn't require the calling code to pass in an empty string. What if they don't - how will they know they made an error? If your recursive call requires it, use an inner function (using let or where).
  4. Where you see a function that looks like foo x y = if (y == ... ) ... else ... it can almost always be improved by either pattern matching or guards.

To expand on point 4, you could rewrite that third line as

strReplace x y z | y !! 0 == fst x = ...
                 | otherwise = ...

Even better, if you took my advice in point 1 and split the tuple into two simple Char parameters, you could do this:

strReplace x1 x2 y@(y1:ys) z | x1 == y = ...
                             | otherwise = ...

Upvotes: 1

daniel gratzer
daniel gratzer

Reputation: 53901

The problem with your code is that z : [snd x] is incorrect, z is a list but : wants it to be an element. This can be fixed by using z ++ [snd x].

If seeing the type signatures helps

(:) :: a -> [a] -> [a]
(++) :: [a] -> [a] -> [a]

Or in your specific case

(:) :: Char -> String -> String
(++) :: String -> String -> String

If I may suggest a few improvements to your code however, first strReplace shouldn't force you to pass an empty string

strReplace :: (Char, Char) -> String -> String

Next, we can do this two ways, using higher order functions or explicit recursion.

-- recursion
strReplace _ "" = "" -- Base case
strReplace (a, b) (c:cs) | a == c = b : strReplace (a,b) cs
                         | otherwise = c : strReplace (a, b) cs

So here if the string is empty we're done, otherwise we pattern match, if the first character is the one to be replaced, we replace it and recurse, otherwise we don't replace it and recurse.

This can actually be done much more cleanly with map though

strReplace (a, b) s = map (\c -> if c == a then b else c) s

This works identically to our previous version, but map abstracts out the looping logic.

Upvotes: 1

Related Questions