Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace an element in a list only once - Haskell

Tags:

list

haskell

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!

like image 719
Afflatus Avatar asked Jan 03 '13 10:01

Afflatus


1 Answers

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:

  1. 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.)

  2. 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
like image 165
dave4420 Avatar answered Nov 29 '22 16:11

dave4420