Search code examples
haskelllist-comprehensionhigher-order-functionsfolddeterminants

How to rewrite this haskell function using folds?


I wrote a haskell function to calculate the determinant of a matrix:

determinant :: Matrix -> Int
determinant [] = 0
determinant [[k]] = k
determinant n = sum [((-1) ^ m) * (head n !! m) * determinant (removeColumn (drop 1 n) m) | m <- [0 .. l]]
  where
    l = length (head n) - 1

It uses the helper function 'removeColumn' to extract the columns:

removeColumn :: Matrix -> Int -> Matrix
removeColumn m n = map (\row -> take (n-1) row ++ drop n row) m

I want to try to rewrite the 'determinant' function using folds. I have been brainstorming for a while but I can't think of how to start. Any help would be appreciated.


Solution

  • It's difficult to convert your version without changing it fundamentally, because it uses an irregular recursion scheme. For folds you would want your function to have a basic structure like this for some z and k:

    determinant [] = z
    determinant (x : xs) = k x (determinant xs)
    

    Your functions recurses on determinant (removeColumn (drop 1 n) m), so it is not obviously compatible.

    Instead of starting with your function, I have taken the liberty to write an implementation based on the Leibniz formula from Wikipedia:

    import Data.Bifunctor (first, bimap)
    
    -- e.g. insertEverywhere 0 [1,2] == [(0,[0,1,2]),(1,[1,0,2]),(2,[1,2,0])]
    -- the tupled integer keeps track of the number of inversions
    insertEverywhere :: a -> [a] -> [(Int, [a])]
    insertEverywhere x = 
      (\(ys,yss) -> (0,(x:ys)):yss) 
        . foldr 
          (\y (ys1,ys2) -> (y:ys1, (1, y:x:ys1):map (bimap (+ 1) (y:)) ys2))
          ([], [])
    
    permutations :: [a] -> [(Int, [a])]
    permutations = 
      foldr 
        (\x xs -> concatMap (\(n, xs) -> map (first (+ n)) (insertEverywhere x xs)) xs) 
        [(0, [])]
    
    determinant :: Num a => [[a]] -> a
    determinant = 
      sum 
        . map (\(i,p) -> (-1) ^ i * product (zipWith (!!) p [0..]))
        . perms
    

    I'd say the essential part of this implementation is the use of the permutations as an intermediate structure (tupled with the number of inversions).