简体   繁体   English

带有嵌套向量的 Data.Vector.modify

[英]Data.Vector.modify with nested vectors

I have a vector nested inside another.我有一个嵌套在另一个向量中的向量。 I want to use modify to update this matrix in place. 我想使用modify来更新这个矩阵。 So I use it for the inner vector, but do I also need to use it for the outer?所以我将它用于内部向量,但我还需要将它用于外部向量吗?

My suggestion from the comments still stands, if you do not need to operate on a ragged array then the usual rectangular array implementation is better.我在评论中的建议仍然有效,如果您不需要对参差不齐的数组进行操作,那么通常的矩形数组实现会更好。 Here is a short list of drawbacks of vector of vectors:以下是向量向量的缺点的简短列表:

  • performance penalty: the outer vector has to be boxed (which means an extra pointer indirection)性能损失:外部向量必须装箱(这意味着额外的指针间接)
  • safety: you can't guarantee the same length of all rows安全:你不能保证所有行的长度相同
  • operating on ragged arrays is cumbersome在参差不齐的数组上操作很麻烦

Nevertheless question still stands: how would you modify a vector of vectors in place.尽管如此,问题仍然存在:您将如何就地修改向量向量。 Below I'll provide an example function, which uses mutation to reverse rows of a ragged array and another function that reverses both rows and columns.下面我将提供一个示例函数,它使用变异来反转参差不齐的数组的行,以及另一个反转行和列的函数。 Difference is that in the former we only mutate elements of each row, while in the latter we also mutate the outer boxed vector that corresponds to rows themselves:不同的是,前者我们只变异每一行的元素,而后者我们也变异对应于行本身的外框向量:

{-# LANGUAGE RankNTypes #-}
import Control.Monad as M
import Control.Monad.ST
import Prelude as P
import Data.Vector as V
import Data.Vector.Generic.Mutable as VGM
import Data.Vector.Mutable as VM
import Data.Vector.Primitive as VP
import Data.Vector.Primitive.Mutable as VPM

raggedModifyRows ::
     VP.Prim a
  => (forall s. V.Vector (VPM.MVector s a) -> ST s ())
  -> V.Vector (VP.Vector a)
  -> V.Vector (VP.Vector a)
raggedModifyRows action arr = runST $ do
  -- thaw will create a copy of each row, so they can be safely modified
  mvs <- V.mapM VP.thaw arr
  action mvs
  -- We are freezing mutated copies, so it is safe to use unsafeFreeze here too
  V.mapM VP.unsafeFreeze mvs

raggedModify ::
     VP.Prim a
  => (forall s. VM.MVector s (VPM.MVector s a) -> ST s ())
  -> V.Vector (VP.Vector a)
  -> V.Vector (VP.Vector a)
raggedModify action arr = runST $ do
  arr' <- V.mapM VP.thaw arr
  -- mapM already created a copy of a boxed vector, so we can use unsafeThaw
  mv <- V.unsafeThaw arr'
  action mv
  v <- V.unsafeFreeze mv
  V.mapM VP.unsafeFreeze v

generateMatrix ::
     Prim a => (Int, Int) -> ((Int, Int) -> a) -> V.Vector (VP.Vector a)
generateMatrix (m, n) f = V.generate m $ \ i -> VP.generate n $ \j -> f (i, j)

generateRagged ::
     Prim a => V.Vector Int -> ((Int, Int) -> a) -> V.Vector (VP.Vector a)
generateRagged v f = V.imap (\ i n -> VP.generate n $ \j -> f (i, j)) v

reverseST :: (VGM.MVector v a) => v s a -> ST s ()
reverseST mv =
  let n = VGM.length mv
   in M.forM_ [0 .. (n `div` 2) - 1] $ \j -> VGM.swap mv j (n - j - 1)

reverseRaggedRows :: Prim a => V.Vector (VP.Vector a) -> V.Vector (VP.Vector a)
reverseRaggedRows = raggedModifyRows $ \rows -> V.forM_ rows reverseST

reverseRagged :: Prim a => V.Vector (VP.Vector a) -> V.Vector (VP.Vector a)
reverseRagged =
  raggedModify $ \mrows -> do
    let reverse' i = VM.read mrows i >>= reverseST
    let m = VM.length mrows
    M.forM_ [0 .. (m `div` 2) - 1] $ \i -> do
      reverse' i
      VM.swap mrows i (m - i - 1)
      reverse' i
    M.when (odd m) $ reverse' (m `div` 2)

Which can be used as follows:可以按如下方式使用:

λ> m = generateMatrix (3, 4) $ \(i, j) -> i+j
λ> m
[[0,1,2,3],[1,2,3,4],[2,3,4,5]]
λ> reverseRaggedRows m
[[3,2,1,0],[4,3,2,1],[5,4,3,2]]
λ> reverseRagged m
[[5,4,3,2],[4,3,2,1],[3,2,1,0]]
λ> m = generateRagged (V.fromList [1,2,3]) $ \(i, j) -> i+j
λ> m
[[0],[1,2],[2,3,4]]
λ> reverseRaggedRows m
[[0],[2,1],[4,3,2]]
λ> reverseRagged m
[[4,3,2],[2,1],[0]]

Alternatively we could have used Data.Vector.modify to operate on the outer vector or map a destructive action that uses modify across all rows.或者,我们可以使用Data.Vector.modify对外部向量进行操作或映射一个破坏性操作,该操作在所有行中使用modify There are all sorts of ways to go about it, depends on what you are trying to achieve, for example:有各种各样的方法可以实现,这取决于您要实现的目标,例如:

λ> m = generateRagged (V.fromList [1,2,3]) $ \(i, j) -> i+j
λ> V.map (VP.modify reverseST) m
[[0],[2,1],[4,3,2]]
λ> V.modify reverseST (V.map (VP.modify reverseST) m)
[[4,3,2],[2,1],[0]]

I did recommend using massiv for regular multidimensional arrays.我确实建议将massiv用于常规多维数组。 Therefore here is also an example of how to achieve the same with withMArrayST :因此,这里也是如何使用withMArrayST实现相同的withMArrayST

{-# LANGUAGE FlexibleContexts #-}
import Control.Monad as M
import Data.Massiv.Array as A

reverseMatrix :: Mutable r Ix2 e => Array r Ix2 e -> Array r Ix2 e
reverseMatrix arr =
  withMArrayST arr $ \marr -> do
    let Sz2 m n = msize marr
        ix2@(m2 :. n2) = m `div` 2 :. n `div` 2
    A.forM_ (0 ..: ix2) $ \ix@(i :. j) -> do
      A.swapM_ marr ix (m - i - 1 :. n - j - 1)
      A.swapM_ marr (i :. n - j - 1) (m - i - 1 :. j)
    when (odd m) $ A.forM_ (0 ..: n2) $ \ j ->
      A.swapM_ marr (m2 :. j) (m2 :. n - j - 1)
    when (odd n) $ A.forM_ (0 ..: m2) $ \ i ->
      A.swapM_ marr (i :. n2) (m - i - 1 :. n2)

Which can be used as follows:可以按如下方式使用:

λ> a = makeArrayR P Seq (Sz2 3 4) $ \ (i :. j) -> i + j
λ> a
Array P Seq (Sz (3 :. 4))
  [ [ 0, 1, 2, 3 ]
  , [ 1, 2, 3, 4 ]
  , [ 2, 3, 4, 5 ]
  ]
λ> reverseMatrix a
Array P Seq (Sz (3 :. 4))
  [ [ 5, 4, 3, 2 ]
  , [ 4, 3, 2, 1 ]
  , [ 3, 2, 1, 0 ]
  ]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM