问题
I want to write a function that updates a single vector element in O(1):
Vector Integer -> Int -> Integer -> Vector Integer
upd v ind x
It's easy to update a value copying the whole vector:
upd v ind x = v // [(ind,x)]
But that's way too slow.
I create a vector as Data.Vector.Generic.fromList
and do not freeze it.
To modify the vector in-place I found function
Data.Vector.modify
, Data.Vector.Mutable.write
and Data.Vector.Mutable.unsafeWrite
, but I can't figure out how to use any of them.
When I try this:
upd v ind x = do DVM.write v ind x
The compiler complains:
Couldn't match type `()' with `Integer'
Expected type: DV.Vector Integer
Actual type: DV.Vector ()
In the return type of a call of `DVM.write'
In a stmt of a 'do' block: DVM.write v ind x
In the expression: do { DVM.write v ind x }
(DV = Data.Vector
, DVM = Data.Vector.Mutable
),
Any help is appreciated.
I'd be extra happy to get an example of using Data.Vector.modify
.
回答1:
First note that a do
block with a single expression is always the same as that expression on its own. So you could just as well have written
upd v ind x = DVM.write v ind x
but this doesn't make sense for a couple of reasons.
v
is still an immutable vector. Mutable vectors are something quite different, they are implemented with references to the state of aPrimMonad
– nothing the like is normally needed for pure Haskell computations, because referential transparency guarantees that the state is always the same. Of course, that's precisely what prevents you from doing updates in O (1), and there's no real way to circumvent that. You need to enter one of those monads to get such updates.- because mutable vectors are “hidden” in the monad's state, you can't simply return them as a function result on their own. That would leak state to the pure-functional language, thus violating referential transparency. You have two options:
- freeze the mutable vector to an immutable one, return that as result. Of course this can again only be done safely with a copy of the whole thing1, so it doesn't gain you anything over the much simpler
//
solution. - stay in the monad as long as you need to do updates. You never freeze the vector as long, it just keeps “floating” in the mutable state. That means you can't have a function signature like
upd
, but need to use only a monadic action. As Louis Wasserman said, this is exactly whatwrite
already does, so really you don't need to do anything more. (Which makes sense: if a function like theupd
you envisioned was possible, it would certainly be already in thevector
library.)
- freeze the mutable vector to an immutable one, return that as result. Of course this can again only be done safely with a copy of the whole thing1, so it doesn't gain you anything over the much simpler
Now, that doesn't quite explain how to use mutable vectors, but to do that we'd need to know the context in which you had hoped to use upd
. However, before you go mutable at all: why are you so sure that a simple pure update is to slow for your purpose? The vector
library is pretty good at “batching” such updates by stream fusion; if you're doing O (n) independent updates then each can on avarage be O (1), because only one copy is made for all the updates.
1And the thawing when you inject the vector into the monad to become mutable may already require a copy, too.
回答2:
What you are asking for is just:
> :t modify (\mv -> write mv 10 'a')
modify (\mv -> write mv 10 'a') :: Vector Char -> Vector Char
> modify (\mv -> write mv 10 'a') (fromList "Hello good comrades")
fromList "Hello goodacomrades"
> modify (\mv -> unsafeWrite mv 0 100) (fromList [1..20::Integer])
fromList [100,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
And continuing with the other operations you mention:
> :t thaw (fromList "Hello good comrades") >>= \mv -> write mv 10 'a'
thaw (fromList "Hello good comrades") >>= \mv -> write mv 10 'a'
:: Control.Monad.Primitive.PrimMonad m => m () -- i.e. IO or ST s
> :t thaw (fromList "Hello good comrades") >>= \mv -> write mv 10 'a' >> freeze mv
thaw (fromList "Hello good comrades") >>= \mv -> write mv 10 'a' >> freeze mv
:: Control.Monad.Primitive.PrimMonad m => m (Vector Char)
> :t thaw (fromList "Hello good comrades") >>= \mv -> write mv 10 'a' >> freeze mv >>= print
thaw (fromList "Hello good comrades") >>= \mv -> write mv 10 'a' >> freeze mv >>= print
:: IO ()
> thaw (fromList "Hello good comrades") >>= \mv -> write mv 10 'a' >> freeze mv >>= print
fromList "Hello goodacomrades"
Now importing Control.Monad.ST
for runST
> :t runST
runST :: (forall s. ST s a) -> a
> :t thaw (fromList "Hello good comrades") >>= \mv -> write mv 10 'a'
thaw (fromList "Hello good comrades") >>= \mv -> write mv 10 'a'
:: Control.Monad.Primitive.PrimMonad m => m () -- we will use ST s
> :t runST $ thaw (fromList "Hello good comrades") >>= \mv -> write mv 10 'a' >> freeze mv
runST $ thaw (fromList "Hello good comrades") >>= \mv -> write mv 10 'a' >> freeze mv
:: Vector Char
> runST $ thaw (fromList "Hello good comrades") >>= \mv -> write mv 10 'a' >> freeze mv
fromList "Hello goodacomrades"
This is why the argument you give to runST
is like the argument you give to modify
.
来源:https://stackoverflow.com/questions/28334189/haskell-update-a-single-vector-element-in-o1