Haskell. update a single Vector element in O(1)

巧了我就是萌 提交于 2021-01-28 03:20:26

问题


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.

  1. v is still an immutable vector. Mutable vectors are something quite different, they are implemented with references to the state of a PrimMonad – 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.
  2. 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 what write already does, so really you don't need to do anything more. (Which makes sense: if a function like the upd you envisioned was possible, it would certainly be already in the vector library.)

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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!