[Haskell] Function to replace given element in list

David Feuer david.feuer at gmail.com
Tue Jul 19 22:13:15 UTC 2016

Using a zipper will not get you very far here. The best way would
likely be to replace the list with a balanced search tree. Sticking
with the list for now, your choice to replace the entire list with [5]
if the sought element is not found seems a bit peculiar, and also
leads to an inherent efficiency problem. First, let me show you what a
more natural function might look like:

replaceFirstMatching :: (a -> Maybe a) -> [a] -> [a]
replaceFirstMatching _ [] = []
replaceFirstMatching p (x : xs) =
  case p x of
     Nothing -> x : replaceFirstMatching p xs
     Just x' -> x' : xs

Taking a function as an argument rather than an element to replace and
a replacement element avoids the risk of mixing up which is which,
while also being somewhat more general. You can simulate the equality
version using it, if you like:

replaceFirstEqual :: Eq a => a -> a -> [a] -> [a]
replaceFirstEqual toReplace replacement = replaceFirstMatching $
  \x -> if x == toReplace then Just replacement else Nothing

Now I mentioned that your [5] fall-back is problematic from an
efficiency standpoint. The reason is that you don't know whether
you'll find the element you desire until you find it or hit the end of
the list. So you can't lazily produce bits of list as you go; you have
to save up the pieces for a while. If you really want to do this, you
certainly can, starting with a modified version of
replaceFirstMatching that indicates whether it found the element:

replaceFirstMatchingM :: (a -> Maybe a) -> [a] -> Maybe [a]
replaceFirstMatchingM _ [] = Nothing
replaceFirstMatchingM p (x : xs) =
  case p x of
    Nothing -> fmap (x:) replaceFirstMatchingM p xs
    Just x' -> Just (x' : xs)

replaceFirstMatchingFallbackList :: (a -> Maybe a) -> [a] -> [a] -> [a]
replaceFirstMatchingFallbackList p fallback xs =
  fromMaybe fallback (replaceFirstMatchingMaybe p xs)

Note that, like replaceFirstEqual, replaceFirstMatchingFallbackList
has arguments that are easily confused.

On Tue, Jul 19, 2016 at 4:42 PM, Carl Folke Henschen Edman
<carledman at gmail.com> wrote:
> On Tue, Jul 19, 2016 at 4:08 PM, Niely Boyken <niely.b0yk3n at gmail.com>
> wrote:
>>     let i = elemIndex toReplace lst in
>>         case i of
>>             Just i ->
>>                 let z = splitAt i lst
>>                     x = fst z
>>                     y = (snd z)
>>                     in
>>                         init x
>>                         x ++ newNmr
>>                         x ++ y
>>             Nothing -> [5]
> If I understand what you are trying to do correctly, a more idiomatic (and
> syntactically correct code) would be:
>    case elemIndex toReplace lst of
>       Just i -> let (xs,_:ys)=splitAt i lst in xs ++ (newNmr:ys)
>       _       -> [5]
> In more general terms, replacing individual elements in standard lists is a
> very inefficient operation for large lists.  Having you considered using a
> Zipper List which allows you to efficiently replace elements at a focal
> point (and also to traverse the list forward and backward efficiently)?
> An example implementation of a Zipper (just for Lists) is at
> https://hackage.haskell.org/package/ListZipper-,
> but implementing your own is an easy and instructive exercise.
>     Carl Edman
> _______________________________________________
> Haskell mailing list
> Haskell at haskell.org
> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell

More information about the Haskell mailing list