Eddy
Eddy

Reputation: 11

Haskell -Changing a Char to another specified Char in a specified position in a String

I have been learning some Haskell and I came up with a solution to one of my exercise which I was trying to figure out .

changeStr :: Int -> Char -> String -> String
changeStr x char zs = (take (x-1) zs) ++ [(changeChar (head (take x zs)) char)] ++ (drop x zs)
changeChar :: Char -> Char -> Char
changeChar x y = y

I just wanted to ask is there any other way in which I could do this in a more simpler way using different methods ?

Upvotes: 2

Views: 334

Answers (2)

J. Abrahamson
J. Abrahamson

Reputation: 74334

The thing that screams for generalization is changeChar. It's actually very close to a very common Haskell Prelude function called const. To get changeChar we just need to flip const.

const :: a -> b -> a
const a b = a

changeChar :: Char -> Char -> Char
changeChar     = flip const
--             = flip (\a _ -> a)
--             = \_ a -> a
--         _ a = a

Beyond that, your code is fairly reasonable, but can be cleaned up by using a function splitAt

splitAt :: Int -> [a] -> ([a], [a])
splitAt n xs           =  (take n xs, drop n xs)

changeChar x char xs = 
  let (before, _it:after) = splitAt (x - 1)
  in before ++ (char:after)

which also highlights a slight problem with this definition in that if your index is too large it will throw a pattern matching failure. We could fix that by making the function return an unmodified string if we "fall off the end"

changeChar x char xs = 
  let (before, after) = splitAt (x - 1)
  in case after of
    []       -> []
    (_:rest) -> char:rest

There's a general pattern here as well of applying a modifying function at a particular place in a list. Here's how we can extract that.

changeAt :: Int -> (a -> a) -> [a] -> [a]
changeAt n f xs = 
  let (before, after) = splitAt (n-1)
  in case after of
    []       -> []
    (x:rest) -> (f x):rest

We can use this to iterate the notion

-- | Replaces an element in a list of lists treated as a matrix.
changeMatrix :: (Int, Int) -> a -> [[a]] -> [[a]]
changeMatrix (i, j) x = changeAt i (changeAt j (const x))

Upvotes: 2

bheklilr
bheklilr

Reputation: 54058

What you have is pretty much what you need, except the function changeChar is just flip const, and you could rewrite yours as

changeStr x char zs = take (x-1) zs ++ [char] ++ drop x zs

If you wanted to be complicated, you could use splitAt from Data.List and the fact that fmap f (a, b) = (a, f b)

changeStr idx c str = uncurry (++) $ fmap ((c:) . tail) $ splitAt (idx - 1) str

And if you wanted to be really complicated, you could ask the pointfree bot how to write it without explicit function arguments

changeStr = ((uncurry (++) .) .) . flip ((.) . fmap . (. tail) . (:)) . splitAt . subtract 1

Upvotes: 1

Related Questions