2014-12-24 58 views
2

我正在尝试创建一个包装可变向量的类型,2D矩阵。我想要一些操作,比如set,get。问题是我完全不知道如何让它的类型工作。我尝试了几件事,但我真的只是在黑暗中拍摄,因为在如何做到这一点上绝对没有任何解释。参见:如何创建一个包装可变向量的类型?

data Matrix v s a = Matrix { size :: (Int,Int), buffer :: v s a } -- ?? Wrong 
newMatrix = ?? 
set (x,y) mat = ?? 
get (x,y) val mat = ?? 

the official documentation为例。构造函数在哪里? vaclass MVector v a where是什么意思?到底是什么m (v (PrimState m) a)?那是泛型可变向量的“类型”吗?什么是mv?什么是PrimState?

+2

'newMatrix'将不得不具有'(PrimMonad m,MVector va)=> arg1 - > arg2 - > argN - > m(Matrix vsa)'类型',因为您需要使用'Data。 Vector.Generic.Mutable.new'来构造它。 – bheklilr

+0

'PrimState'是一个与'PrimMonad'关联的类型。只有两个'PrimMonad'实例:'ST s'和'IO'。因此'PrimState'或者是'RealWorld'或's'(来自'STs')。但是这些东西隐藏在['Control.Monad.Primitive']中(http://hackage.haskell.org/package/primitive-0.2.1/docs/Control-Monad-Primitive.html#t:PrimMonad)。 – Zeta

+0

注意:您链接的文档很古老。这是2010年的“矢量0.5”。看看['vector 0.10.12。*'](http://hackage.haskell.org/package/vector-0.10.12.2/docs/Data-Vector-Mutable.html ),相反,虽然类型仍然大体相同。 – Zeta

回答

3

正如@ user2407038指出的那样,首先将自己限制在一个具体的MVector类型如IOVector更容易。如果你擅长的newreadwriteIOVector类型,您会收到以下方式中,特别是非恐吓类型:

new :: Int -> IO (IOVector a) 
read :: IOVector a -> Int -> IO a 
write :: IOVector a -> Int -> a -> IO() 

所以对于第一个版本,你Matrix操作的实现是直截了当:

import Data.Vector.Mutable as V 
import Control.Monad (liftM) 

data Matrix a = Matrix { size :: (Int, Int), buffer :: IOVector a } 

newMatrix :: (Int, Int) -> IO (Matrix a) 
newMatrix (w, h) = liftM (Matrix (w, h)) $ V.new (w * h) 

set :: (Int, Int) -> a -> Matrix a -> IO() 
set pos e mtx = V.write (buffer mtx) (offset mtx pos) e 

get :: (Int, Int) -> Matrix a -> IO a 
get pos mtx = V.read (buffer mtx) (offset mtx pos) 

offset :: Matrix a -> (Int, Int) -> Int 
offset (Matrix (w, _h) _) (x, y) = w * y + x 

那么,我们如何推广MVector s中的sMatrix本身需要一概而论超过s选择:

data Matrix s a = Matrix { size :: (Int, Int), buffer :: MVector s a } 

而且我们还需要通过线程这个推广到所有的功能。我们来看看newMatrix的细节;其余的可以留给读者作为练习。

如果我们只是抽象的snewMatrix成为

newMatrix :: (Int, Int) -> IO (Matrix s a) 
newMatrix (w, h) = liftM (Matrix (w, h)) $ V.new (w * h) -- Same implementation as above 

但是,这肯定不可能是正确的 - 我们不能为s任何选择,只有RealWorld创建IOMVector s a!果然,在typechecker抓住这样的:

Couldn't match type `s' with `RealWorld' 
    `s' is a rigid type variable bound by 
     the type signature for newMatrix :: (Int, Int) -> IO (Matrix s a) 
Expected type: s 
    Actual type: PrimState IO 
Expected type: IO (MVector s a) 
    Actual type: IO (MVector (PrimState IO) a) 
In the return type of a call of `new' 
In the second argument of `($)', namely `new (w * h)' 

但假设我们写

newMatrix :: (Monad m) => (Int, Int) -> m (Matrix s a) 

这一点,在某种意义上说,更糟糕的是,现在我们是在说为ms任何选择(独立的彼此!),我们可以在m中构建Matrix s a。显然情况并非如此。

这是需要的PrimMonad类型类,其中:它提供PrimState m之间的联系,的s为向量选择被操纵,并且单子m其中该操作是可能的。 newMatrix从而成为

newMatrix :: (PrimMonad m) => (Int, Int) -> m (Matrix (PrimState m) a) 
newMatrix (w, h) = liftM (Matrix (w, h)) $ V.new (w * h) -- implementation is still the same! 

其它操作可以以类似的方式被键入。

+0

辉煌。考虑到互联网上的主题的状态,我敢打赌,很多人会从你的帖子中学习,来自谷歌。 – MaiaVictor

相关问题