pid
pid

Reputation: 181

Replace multiple elements of a list

I am trying to redo the following (simplified) python example in Haskell

zerosList = [0,0,0,0,0,0,0,0,0,0]
for number in range(1,10):
  if (number & 1) == 0:
    for index in range(number,9,2):
      zerosList[index] = zerosList[index]+1

where we change some values in the list from [0,0,0,0,0,0,0,0,0,0] to [0,0,1,0,2,0,3,0,4,0].

I suppose I have to use some filters to capture even numbers and then probably use a map or a foldl somewhere... but can't figure out how to assemble the whole thing. Something like that?

zeros   = take 10 $ cycle [0]
numbers = [1..10]
foldl (+) zeros $ filter even numbers

or that?

zeros   = take 10 $ cycle [0]
numbers = [1..10]
map (+ $ filter even numbers) zeros

Well, none of that works so I am clearly on the wrong path. Maybe I should write a replaceAt function?

Upvotes: 1

Views: 254

Answers (2)

Chris
Chris

Reputation: 36451

You may be overthinking this. When you understand how pattern-matching lists works, this becomes fairly straightforward.

replace :: Int -> [Int] -> [Int]
replace _ [] = []
replace i [x] = [i]
replace i (x:y:xs) = i : y : replace (i+1) xs

We can match an empty list, and that should obviously return an empty list. Since the pattern is every even index being replaced by an increasing number, starting at 0, we know a list with a single element is just replaced by a list with that increasing int.

Now, we can also pattern match the first two elements of a list, and the tail. We simply replace the first one with the increasing int, don't modify the second, and cons it all onto the result of calling the function on the tail while increasing the int by one.

ghci> replace 0 [0,0,0,0,0,0,0,0,0,0]
[0,0,1,0,2,0,3,0,4,0]

Passing in the initial zero could be easily hidden.

replace :: [Int] -> [Int]
replace = replace' 0
  where 
    replace' _ [] = []
    replace' i [x] = [i]
    replace' i (x:y:xs) = i : y : replace' (i+1) xs

Upvotes: 1

amalloy
amalloy

Reputation: 91837

The most straightforward way to get the output list you want is to observe that it is very predictable: it's just an interleaving of [0..4] and repeat 0.

concat $ zipWith (\a b -> [a, b]) [0..4] (repeat 0)

You may object that this isn't implementing your algorithm, it's making use of some insight into your simplification that doesn't exist in your real data. Well, too bad, I would say. Porting an imperative algorithm to a functional language often requires thinking about the problem differently. If my solution is too specialized to work for your real inputs, consider providing more realistic inputs.

Upvotes: 3

Related Questions