Home > Mobile >  Haskell: Change the (n,m)th element in a Lists in List
Haskell: Change the (n,m)th element in a Lists in List

Time:11-24

The replaceAtMatrix function should take in a tuple (Int,Int) that decides what position is the value that needs to be changed, a value to be changed to, and the List in List [[a]].

If the tuple's values exceed the lists length the original [[a]] needs to be returned.

replaceAtList :: Int -> a -> [a] -> [a] 
replaceAtList _ _ [] = []
replaceAtList 0 a (_:xs) = a:xs
replaceAtList number a (x:xs)
    | number < 0 = x:xs
    | number > (length (x:xs)-1) = x:xs 
    | otherwise = x : replaceAtList (number-1) a xs

replaceAtMatrix :: (Int,Int) -> a -> [[a]] -> [[a]]
replaceAtMatrix _ _ [] = []
replaceAtMatrix (0,num2) a (x:xs) = replaceAtList num2 a x : xs
replaceAtMatrix (num1,num2) a (x:xs)
    | num1 < 0 = x:xs
    | num1 > (length (x:xs)-1) = x:xs
    | num2 < 0 = x:xs
    | num2 > (length (x:xs)-1) = x:xs 
    | otherwise = x : replaceAtMatrix (num1-1,num2) a xs

There are test for which I checked, and it passes most of them, but there's one where it goes in an infinite cycle. Here's the tests that work.

replaceAtMatrix (-1,0) 'A' ["bcd","lds","rtz"] == ["bcd","lds","rtz"]
replaceAtMatrix (0,0) 'A' ["bcd","lds","rtz"] == ["Acd","lds","rtz"]
replaceAtMatrix (2,0) 'A' ["bcd","lds","rtz"] == ["bcd","lds","Atz"]
replaceAtMatrix (3,0) 'A' ["bcd","lds","rtz"] == ["bcd","lds","rtz"]
replaceAtMatrix (2,2) 'A' ["bcd","lds","rtz"] == ["bcd","lds","rtA"]

And here's the one that doesn't.

Edit: I just realized it's probaly beacuse I use length on an infinite list. How can I rewrite it without using length?

take 5 (replaceAtMatrix (1,2) 100 [[1..],[1,11..],[],[12,13,14]] !! 1) == [1,11,100,31,41]

Without the guard, I get a variable not in scope error.

replaceAtList :: Int -> a -> [a] -> [a] 
replaceAtList _ _ [] = []
replaceAtList 0 a (_:xs) = a:xs
replaceAtList number a (x:xs) = x : replaceAtList (number-1) a xs

replaceAtMatrix :: (Int,Int) -> a -> [[a]] -> [[a]]
replaceAtMatrix _ _ [] = []
replaceAtMatrix (0,num2) a (x:xs) = replaceAtList num2 a x : xs
replaceAtMatrix (num1,num2) a (x:xs) = x : replaceAtMatrix (num1-1,num2) a xs

CodePudding user response:

Don't use length. This will error on infinite lists: you do not need this: in case the number > length somelist, eventually it will reach the end of the list:

replaceAtList :: Int -> a -> [a] -> [a] 
replaceAtList _ _ [] = []
replaceAtList n _ xs
    | n < 0 = xs
replaceAtList 0 a (_:xs) = a:xs
replaceAtList number a (x:xs) = x : replaceAtList (number-1) a xs

replaceAtMatrix :: (Int,Int) -> a -> [[a]] -> [[a]]
replaceAtMatrix _ _ [] = []
replaceAtMatrix (m, _) _ xs
    | m < 0 = xs
replaceAtMatrix (0,num2) a (x:xs) = replaceAtList num2 a x : xs
replaceAtMatrix (num1,num2) a (x:xs) = x : replaceAtMatrix (num1-1,num2) a xs

CodePudding user response:

I think you have your answer, and can make progress, but I thought it might be interesting to show you an alternate implementation. There's some repetition in your existing code of traversing a list to a specific spot, and then making a change. Here's an idea for how you might implement that just once:

onIndex :: Int -> (a -> a) -> ([a] -> [a])
onIndex n f = if n < 0 then id else loop n where
    loop _ [] = []
    loop 0 (x:xs) = f x:xs
    loop n (x:xs) = x:loop (n-1) xs

Rather than taking an element and replacing that element, this takes an arbitrary editing function and applies that function to the element at the appropriate position. We can use this to replace an element with value foo using const foo as the editing function. But, and this is the key thing, we can also use an editing function that itself calls onIndex!

replaceAtMatrix :: (Int, Int) -> a -> [[a]] -> [[a]]
replaceAtMatrix (m, n) = onIndex m . onIndex n . const
  • Related