user3023610
user3023610

Reputation: 51

replacing an element in a list of lists in haskell

I am trying to write a function like this:

updateMatrix:: [[a]] -> a -> (x, y) ->[[a]]

This is supposed to take in a list of lists such as:

[ [1, 2, 3, 4],
  [5, 6, 7, 8]]

and put the given element at the specified coordinates, so, given:

[ [1, 2, 3, 4],
  [5, 6, 7, 8]] 9 (0, 1)

it should return

[ [1, 9, 3, 4],
  [5, 6, 7, 8]]

I can't figure out how to do this without having to rebuild the whole matrix, please help!

Upvotes: 2

Views: 4360

Answers (4)

anon
anon

Reputation:

Here’s a short one:

replace p f xs = [ if i == p then f x else x | (x, i) <- zip xs [0..] ]
replace2D v (x,y) = replace y (replace x (const v))

Now you can use it exactly like you wanted:

 λ →  let m = [[1, 2, 3, 4], [5, 6, 7, 8]]
 λ →  replace2D 9 (0, 1) m
[[1,2,3,4],[9,6,7,8]]

As others already said,

  1. This approach is of course rather slow, and only makes sense if the structure is more complex than the lists are long. There’s easy documentation about the internal structure and complexity of things in Haskell out there.
    Think of m as a pointer to a linked list of pointers, and you can see why it’s slower than a pure stream of bytes. There are better libs that use something closer to the latter.
  2. Haskell’s values are immutable because there are no side-effects. Which is good for reliability. So you can’t change m. You can only build something out of m.
  3. Haskell can simulate mutable references, with the help of monads. Like IORef. But using it for this would be rather wrong. There are many other questions here on Stack Overflow, explaining its usage, pros and cons.

Upvotes: 3

vek
vek

Reputation: 1543

You need to rebuild the matrix every time. So as long as you don't need high performance computing, you could use this legible implementation:

replace :: (a -> a) -> Int -> [a] -> [a]
replace f 0 (x:xs) = (f x):xs
replace f i (x:xs) = x : replace f (i-1) xs
replace f i [] = []

replace2D :: (a -> a) -> (Int, Int) -> [[a]] -> [[a]]
replace2D f (x,y) = replace (replace f y) x

Your function would be:

updateMatrix ll x c = replace2D (const x) c ll

Upvotes: 6

ownclo
ownclo

Reputation: 307

Being a purely functional language, Haskell requires you to return a "brand new" matrix when you update an item, so you need to rebuild the whole matrix indeed (if you're actually interested in matrix processing, cast a look at matrix library rather than implementing your own).

Beware, lists are not a good choice for such manipulations, but if you do it for educational purposes, start with implementing a function that "replaces" an element in [a], then use it twice (function composition can help there) in order to get your updateMatrix function. Here is an answer that can help you on your way.

Upvotes: 1

ntc2
ntc2

Reputation: 11912

Here's an implementation:

updateMatrix :: [[a]] -> a -> (Int, Int) -> [[a]]
updateMatrix m x (r,c) =
  take r m ++
  [take c (m !! r) ++ [x] ++ drop (c + 1) (m !! r)] ++
  drop (r + 1) m

But maybe this "rebuilds the whole matrix" as you say? Note that lists are not mutable in Haskell, so you can't destructively update one entry, if that's what you would mean by not "rebuilding the whole matrix".

Upvotes: 4

Related Questions