2

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!

2
  • 4
    It's not possible to do it without 'rebuilding' the matrix since everything in Haskell is immutable. Still, 'rebuilding' the matrix won't be costly since the compiler will take care of this, it won't literally create another whole new matrix. Commented Nov 22, 2013 at 23:24
  • You cannot do this without rebuilding the matrix, as lists are immutable in Haskell. Commented Nov 22, 2013 at 23:24

4 Answers 4

6

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
Sign up to request clarification or add additional context in comments.

2 Comments

This would be way more elegant with a foldr inside a foldr.
Every f … = … x : f xs is a fold. And the counter could go to the init value. But I found an even better solution in the meantime, and posted it as an answer below.
4

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".

2 Comments

This crashes when r > length m, and will produce wrong results if either coordinate is out of bounds.
@Evi1M4chine: garbage in garbage out :P I agree that in production code we may want to do input validation, but it's easy to add a wrapper that does this; I think it's strange that you down voted over this. It's nice that Vektorweg's solution (and your minor variation of it) is the identity on out-of-bounds coordinates, but in some scenarios we'd probably want to fail fast in this case, and so we're all wrong.
3

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.

2 Comments

Verktorweg: I assumed that a person who codes Haskell doesn’t have the comprehension of a PHP fiddler. This is very easy code to a Haskell coder. Who plays with monad transformer combinators in his sleep. :)
its 4 years later and I still prefer list monads over list comprehensions. so yeah, i have to look at least twice when its list comp, as i'm not used to it and never will. note that i was indeed just beginning with haskell at that time and i would do things differently now. i think these two following solutions fit better than yours and mine now. replace f i = zipWith (\j x -> if i == j then f x else x) [0..] or probably even better replace' f i xs = ys ++ [f x] ++ zs where (ys,x:zs) = splitAt i xs . on the other hand, what is a helpful solution for someone who tries to learn?
1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.