As there are several possibilities to represent the input matrix, we can try to separate the “navigation”, i.e. choice of elements, from the actual matrix representation.
In order to achieve this, we can easily write a recursive function that produces the 2D list of Cartesian coordinates to be extracted from the input matrix:
{-# LANGUAGE TupleSections #-}
-- returns 2D list of Cartesian coordinates for entries of L-shaped matrix:
coordList :: Int -> [[(Int,Int)]]
coordList n = go n 0 n where -- rl: Row Length sr: Starting Row
go n sr rl = ((map (sr,) [0..(rl-1)]) ++ (map (,rl-1) [(sr+1)..(n-1)]) ) :
if (rl > 1) then go n (sr+1) (rl-1) else []
Checking under the ghci interpreter:
λ>
λ> coordList 3
[[(0,0),(0,1),(0,2),(1,2),(2,2)],[(1,0),(1,1),(2,1)],[(2,0)]]
λ>
Next, we test our new coordList function by naïvely using the inefficient !! list extraction operator:
λ> printAsLines xs = mapM_ (putStrLn . show) xs
λ>
λ> xss = [[1,2,3], [4,5,6], [7,8,9]]
λ>
λ> printAsLines $ map (map (\(i,j) -> ((xss !! i) !! j))) $ (coordList 3)
[1,2,3,6,9]
[4,5,8]
[7]
λ>
This might be inefficient, but it is correct. And so we can have a more efficient version of this by replacing lists by vectors and the list !! operator by the equivalent vector ! operator:
import qualified Data.Vector as V
-- vector-based version:
lShapedTraverse :: [[a]] -> [[a]]
lShapedTraverse xss =
let
rank = length (head xss) -- matrix rank
pairs = coordList rank -- 2D list of Cartesian coordinates
matrix = V.fromList (map V.fromList xss) -- 2D vector
in
map (map (\(i,j) -> ((matrix V.! i) V.! j))) $ pairs
Test program:
printAsLines :: Show α => [α] -> IO ()
printAsLines xs = mapM_ (putStrLn . show) xs
main :: IO ()
main = do
let xss = [[1,2,3], [4,5,6], [7,8,9]]
lMat1 = lShapedTraverse xss
putStrLn $ "res1 = "
printAsLines lMat1
Program output:
res1 =
[1,2,3,6,9]
[4,5,8]
[7]