haskellvectorhaskell-vector

Haskell create vector with subvectors using indexes


I'm trying to create a vector with subvectors consisting of elements taken out from another vector using a vector of sub-vector indexes. Each element in b corresponds to the sub-vector-index the elements in a should have when put into c.

import Data.Vector
let a = fromList [9,2,3,7,4,1,8,5]
let b = fromList [3,3,2,0,1,1,2,2]
let c = fromList [ a ! k | k <- b ]
Expected c = [[7],[4,1],[3,8,5],[9,2]]

I'm a bit stuck, getting the error

"Could not match expected type [Int] with actual type Vector Integer in stmt list comprehension k <- b"


Solution

  • This doesn't work since b is a Vector, not a list:

    k <- b
    

    However, this can work:

    [ ... | k <- toList b ]
    

    Next, the type of a and b is Vector Integer, and the ! operator takes an Int. So you need to convert the index using fromInteger:

    let c = fromList [ a ! fromInteger k | k <- toList b]
    

    Update

    Here is a way to perform the transformation without repeated passes over the arrays:

    import Data.List
    
    fst3  (b,_,_) = b
    third (_,_,a) = a
    
    doit :: Vector Int -> Vector Int -> [[Int]]
    doit av bv = [ map third g | g <- groups ]
      where
        triples = zip3 (V.toList bv) [1..] (V.toList av)
        groups = groupBy (\s t -> fst3 s == fst3 t) $ sort triples
    

    This is basically a Schwartzian Transform with a groupBy added after the sorting step. The sorting on the triples is done in the canonical way - a lex sort on the first coordinate followed by the second coordinate followed by the third coordinate.

    There are other ways to write the expression for groups:

    import Data.Funcition (on)
    import GHC.Exts (groupWith)
    
        ...
        groups = groupBy (on (==) fst3) $ sort triples
        groups = groupWith fst3 triples
    

    Note that groupBy requires that the triples be sorted whereas groupWith doesn't.