我的程序需要一个与Hackage上的Vector类型完全相同的Matrix类型,除了使用2D点而不是Ints进行索引。我已经开始创建这样的扩展,如下所示:
module Data.Matrix.Mutable where
import Control.Monad.Primitive
import Control.Monad.ST
import Linear
import qualified Data.Vector.Unboxed.Mutable as MUV
import qualified Data.Vector.Unboxed as UV
data MMatrix s t = MMatrix { dim :: V2 Int, buffer :: MUV.MVector s t }
indexToPos :: Int -> Int -> V2 Int
indexToPos width index = V2 (quot index width) (mod index width)
posToIndex :: Int -> V2 Int -> Int
posToIndex width (V2 x y) = y * width + x
width :: MMatrix s t -> Int
width (MMatrix (V2 w _) _) = w
height :: MMatrix s t -> Int
height (MMatrix (V2 _ h) _) = h
new :: (PrimMonad m, MUV.Unbox t) => V2 Int -> m (MMatrix (PrimState m) t)
new (dim@(V2 w h)) = return . MMatrix dim =<< MUV.new (w * h)
write :: (PrimMonad m, MUV.Unbox t) => MMatrix (PrimState m) t -> V2 Int -> t -> m ()
write matrix pos val = MUV.write (buffer matrix) (posToIndex (width matrix) pos) val
read :: (PrimMonad m, MUV.Unbox t) => MMatrix (PrimState m) t -> V2 Int -> m t
read matrix pos = MUV.read (buffer matrix) (posToIndex (width matrix) pos)
关于进展,我注意到我被迫为每个单一的矢量类型为每个函数手动编写一个包装器。这个过程不仅繁琐,而且维护成本高,因为他们的API的任何变化都需要对我的代码进行同样大的改动。在动态语言中,解决方案更简单:在Vector基类型上覆盖“读取”和“写入”功能。示例(在JavaScript中):
Matrix = clone(Vector);
Matrix.read = function(x,y){
return Vector.read.call(this, this.width * y + x);
};
Matrix.set = function(x,y,val){
return Vector.write.call(this, this.width * y + x, val);
};
从现在开始,Matrix的工作原理与Vector完全相同,除非按照预期由2D点索引。什么是Haskell的等效策略(我想使用类型类)?