Swap two elements in a list of lists based on indices in Haskell

Viewed 417

Is there any way to swap items in a list of lists based on the index of one item? I've found a solution on how to implement swap onto a single list:

swap :: Int -> Int -> [a] -> [a]
swap i j list = let elemI = list !! i
                            elemJ = list !! j
                            left = take i list
                            middle = take (j - i - 1) (drop (i + 1) list)
                            right = drop (j + 1) xs
                    in  left ++ [elemJ] ++ middle ++ [elemI] ++ right

From here I've tried to implement my own solution using a tuple as input for an index:

swapUp :: [(Int, Int)] -> [[Int]] -> [[Int]]
swapUp [(i,j)] puz = let emptyTile = puz !! [(i,j)]
          tile = puz !! [(i+1,j)]
          beg = take [(i,j)] puz
          middle = take [(i+1,j-1)] (drop [(i,j+1)] puz)
          end = drop [(i+1,j)] puz
          in beg ++ tile ++ middle ++ emptyTile ++ end

The basic idea for my function is that it takes in the index of 0 in a list of lists and returns that list of lists with 0 and the number below swapped. I'm not sure if I can technically use the take, drop, and !! syntax since they take in ints but any advice on this would be beneficial.

1 Answers

It might be helpful to first write a function that replaces an element of a list:

replace :: Int -> a -> [a] -> [a]
replace i x lst = take i lst ++ [x] ++ drop (i+1) lst

so (remembering that a string is just a list of characters), we have:

> replace 2 'q' "abcdefg"
"abqdefg"

This makes it easy to write swap without having to do a lot of "index math":

swap :: Int -> Int -> [a] -> [a]
swap i j lst = replace i (lst !! j) $ replace j (lst !! i) $ lst

giving:

> swap 2 4 "abcdefg"
"abedcfg"

It also makes it easy to write a function that replaces an element in a list of lists:

replace2 :: (Int, Int) -> a -> [[a]] -> [[a]]
replace2 (i,j) x lst = replace i (replace j x (lst !! i)) lst

See how this works? It grabs the ith row of lst, namely lst !! i, and uses replace to replace its jth column element with x. Then, it takes that new row and replaces the ith row of lst with it:

> replace2 (1,2) 99 [[10,20,30],[40,50,60],[70,80,90]]
[[10,20,30],[40,50,99],[70,80,90]]

Now, we can swap two elements of a list of lists by writing swap2 using replace2 much the same was we wrote swap using replace:

swap2 :: (Int, Int) -> (Int, Int) -> [[a]] -> [[a]]
swap2 (i1,i2) (j1,j2) lst
  = replace2 (i1,i2) (lst !! j1 !! j2)
  $ replace2 (j1,j2) (lst !! i1 !! i2)
  $ lst

so that:

> swap2 (0,0) (1,2) [[10,20,30],[40,50,60],[70,80,90]]
[[60,20,30],[40,50,10],[70,80,90]]

and swapUp can be defined as:

swapUp :: (Int, Int) -> [[a]] -> [[a]]
swapUp (i,j) = swap2 (i,j) (i+1,j)

so that:

> swapUp (1,1) [[1,2,3],[4,0,6],[7,8,9]]
[[1,2,3],[4,8,6],[7,0,9]]
Related