I want to replace an element in a list with a new value only at first time occurrence. I wrote the code below but using it, all the matched elements will change.
replaceX :: [Int] -> Int -> Int -> [Int]
replaceX items old new = map check items where
check item | item == old = new
| otherwise = item
How can I modify the code so that the changing only happen at first matched item?
Thanks for helping!
The point is that map
and f
(check
in your example) only communicate regarding how to transform individual elements. They don't communicate about how far down the list to transform elements: map
always carries on all the way to the end.
map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs
Let's write a new version of map
--- I'll call it mapOnce
because I can't think of a better name.
mapOnce :: (a -> Maybe a) -> [a] -> [a]
There are two things to note about this type signature:
Because we may stop applying f
part-way down the list, the input list and the output list must have the same type. (With map
, because the entire list will always be mapped, the type can change.)
The type of f
hasn't changed to a -> a
, but to a -> Maybe a
.
Nothing
will mean "leave this element unchanged, continue down the list"Just y
will mean "change this element, and leave the remaining elements unaltered"So:
mapOnce _ [] = []
mapOnce f (x:xs) = case f x of
Nothing -> x : mapOnce f xs
Just y -> y : xs
Your example is now:
replaceX :: [Int] -> Int -> Int -> [Int]
replaceX items old new = mapOnce check items where
check item | item == old = Just new
| otherwise = Nothing
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With