Data.Vector.modify 带有嵌套向量
Data.Vector.modify with nested vectors
我有一个向量嵌套在另一个向量中。 I want to use modify
就地更新此矩阵。所以我将它用于内部向量,但我是否也需要将它用于外部向量?
我在评论中的建议仍然有效,如果您不需要对参差不齐的数组进行操作,那么通常的矩形数组实现会更好。以下是 vector of vector 的缺点的简短列表:
- 性能损失:外部向量必须装箱(这意味着额外的指针间接)
- 安全:您不能保证所有行的长度相同
- 在参差不齐的数组上操作很麻烦
尽管如此,问题仍然存在:您将如何就地修改向量的向量。下面我将提供一个示例函数,它使用突变来反转参差不齐的数组的行,以及另一个函数来反转行和列。不同之处在于,前者我们只改变每一行的元素,而后者我们还改变对应于行本身的外框向量:
{-# 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)
可以这样使用:
λ> 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]]
或者,我们可以使用 Data.Vector.modify
对外部向量进行操作,或者映射一个在所有行中使用 modify
的破坏性操作。有各种各样的方法可以解决这个问题,具体取决于您要实现的目标,例如:
λ> 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]]
我确实推荐使用 massiv
for regular multidimensional arrays. Therefore here is also an example of how to achieve the same with 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)
可以这样使用:
λ> 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 ]
]
我有一个向量嵌套在另一个向量中。 I want to use modify
就地更新此矩阵。所以我将它用于内部向量,但我是否也需要将它用于外部向量?
我在评论中的建议仍然有效,如果您不需要对参差不齐的数组进行操作,那么通常的矩形数组实现会更好。以下是 vector of vector 的缺点的简短列表:
- 性能损失:外部向量必须装箱(这意味着额外的指针间接)
- 安全:您不能保证所有行的长度相同
- 在参差不齐的数组上操作很麻烦
尽管如此,问题仍然存在:您将如何就地修改向量的向量。下面我将提供一个示例函数,它使用突变来反转参差不齐的数组的行,以及另一个函数来反转行和列。不同之处在于,前者我们只改变每一行的元素,而后者我们还改变对应于行本身的外框向量:
{-# 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)
可以这样使用:
λ> 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]]
或者,我们可以使用 Data.Vector.modify
对外部向量进行操作,或者映射一个在所有行中使用 modify
的破坏性操作。有各种各样的方法可以解决这个问题,具体取决于您要实现的目标,例如:
λ> 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]]
我确实推荐使用 massiv
for regular multidimensional arrays. Therefore here is also an example of how to achieve the same with 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)
可以这样使用:
λ> 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 ]
]