Reputation: 1186
I am looking for a function that replaces a character at a given index.
For example:
replace 4 '!' "Hello World!"
-- Output: "Hell! World!"
Upvotes: 0
Views: 1060
Reputation: 530940
There are 4 cases to consider:
In case 1, we return the input unchanged:
replace k _ xs | null xs || k < 0 = xs
(This could be split into two subcases
replace _ _ [] = []
replace k _ xs | k < 0 = xs
)
Case 2 is implicitly handled by recursing until we reach Case 1.
Case 3 is handled by simply returning the replacement character prepended to the tail of the input.
replace 0 c (x:xs) = c : xs
Case 4 is the interesting case: replacing the k
th element of a list is the same as prepending the head of the list to the result of replacing the k-1
th element of the tail.
replace k c (x:xs) = x : replace (k-1) c xs
When k
is too big, we eventually reach Case 1, since xs
will become null before k
becomes 0, bypassing Case 3.
Upvotes: 1
Reputation: 476557
You can improve it slighly by using replacement : strAfter
, and using a safe tail function to prevent an error if the index is greater than the length of the string:
safeTail :: [a] -> [a]
safeTail [] = []
safeTail (_:xs) = xs
replaceCharAtIndex :: Int -> Char -> String -> String
replaceCharAtIndex index replacement str = strHead ++ replacement : safeTail strAfter
where (strHead, strAfter) = splitAt index str
One however should be careful: for negative indices, it will replace the first character of the string. For indices that are greater than the length of the string, it will append to the string. Both cases are not per se the desired behavior.
We can alter the behavior by checking for negative indexes, and matching on the empty tail:
replaceCharAtIndex :: Int -> Char -> String -> String
replaceCharAtIndex index replacement str
| index < 0 = … -- index located before the string
| (_:xs) <- strAfter = strHead ++ replacement : xs
| otherwise = … -- index located after the string.
where (strHead, strAfter) = splitAt index str
Here we thus have two …
s to fill in a result for these edge-cases.
Upvotes: 1
Reputation: 1186
In case somebody is looking for such a function or a similar one, I came up with this:
replaceCharAtIndex :: Int -> Char -> String -> String
replaceCharAtIndex index replacement str = strHead ++ [replacement] ++ drop 1 strAfter
where (strHead, strAfter) = splitAt index str
Usage: replaceCharAtIndex 4 '!' "Hello World!"
Upvotes: 1