考虑这个例子:
import qualified Data.Vector.Unboxed as Vector
import Data.Vector.Unboxed (Vector)
import qualified Data.Vector.Unboxed.Mutable as MVector
process :: [Int] -> Vector Int -> Vector Int
process [] v = v
process (x:xs) v = process xs $ Vector.modify modify v
where
modify mv = do
old <- MVector.read mv x
MVector.write mv x (old + 1)
main :: IO ()
main = do
print $ process [1, 1, 3, 1] $ Vector.replicate 10 0
在每次迭代的core
中,我看到newByteArray#
、copyByteArray#
、readIntArray#
、writeIntArray#
和unsafeFreezeByteArray#
的序列。它肯定会创建中间副本。
文档指出:
对矢量应用破坏性操作。操作将是 如果安全,请就地执行,并将修改 否则向量。
它实现为
modify p = new . New.modify p . clone
还有RULE
"clone/new [Vector]" forall p.
clone (new p) = p
根据我(非常有限的)理解,两个连续modify
不应该创建中间副本:
modify p2 . modify p1
=> new . New.modify p2 . clone . new . New.modify p1 . clone
=> new . New.modify p2 . New.modify p1 . clone
为什么它在process
函数中不起作用?我认为它应该:
process [1, 2] v
=> process [2] $ Vector.modify modify v
=> process [] $ Vector.modify modify $ Vector.modify modify v
=> Vector.modify modify $ Vector.modify modify v
=> the same as above
我怎样才能让它工作?
递归阻止了它。
源代码中没有任何地方说modify
紧跟modify
。函数的每次迭代都会再次调用modify
,但这发生在运行时,而不是编译时。我认为这就是问题所在。如果你在一行中手动写入三个modify
调用,它们会融合。
我想不出自动制作这个保险丝的方法。您当然可以手动调用clone
并自己new
,但我想不出让编译器为您执行此操作的方法。
你为什么不只产生一个调用modify
?
import Control.Monad (forM_)
[...]
process :: [Int] -> Vector Int -> Vector Int
process xs v = Vector.modify modify v
where
modify mv = forM_ xs $ x -> do
old <- MVector.read mv x
MVector.write mv x (old + 1)