我认为没有内置函数可以满足您的需求。但是使用这些原语应该可以为您的任务编写命令式代码。如果它足够通用,你甚至应该可以在纯代码中使用它create
。我想它会是这样的(只是草图,未经测试):
imapInto :: (PrimMonad m, Unbox a) => (MVector (PrimState m) a -> Int -> a) -> MVector (PrimState m) a -> MVector (PrimState m) a -> m ()
imapInto f d s = go 0
where
go i = when (i < length d) $ write d i (f s i) >> go (i+1)
不过,您的映射函数类型似乎很奇怪。你不是说a -> Int -> a
吗?那么上面的代码将需要一些小的改动。
更新
这是使用示例,以及上述函数的更新版本。最不可能的调整是将m
类型构造函数添加到映射器函数:
module Main where
import qualified Data.Vector.Unboxed.Mutable as MV
import qualified Data.Vector.Unboxed as U
import Control.Monad (forM_)
import Control.Monad.Primitive
imapInto :: (PrimMonad m, MV.Unbox a)
=> (MV.MVector (PrimState m) a -> Int -> m a)
-> MV.MVector (PrimState m) a
-> MV.MVector (PrimState m) a
-> m ()
imapInto f d s = forM_ [0..MV.length s - 1] $ \i -> do
v <- f s i
MV.write d i v
main = do
-- Create two vectors
v1 <- MV.replicate 10 1
v2 <- MV.new 10
-- Map v1 into v2 using function mapper defined below
imapInto mapper v2 v1
-- Print the source and the result
uv1 <- U.unsafeFreeze v1
uv2 <- U.unsafeFreeze v2
print $ U.toList uv1 -- [1,1,1,1,1,1,1,1,1,1]
print $ U.toList uv2 -- [0,1,2,3,4,5,6,7,8,9]
where
-- Mapper reads a value from the vector and multiplies it by its index
mapper v i = fmap (*i) $ MV.read v i
由于您是初学者,因此我尝试尽可能简单,请询问是否有不清楚的地方。
如您所见,我利用了 Louis 的评论并使用forM_
函数来使更imapInto
简单(交换了参数)。它现在看起来就像来自命令式语言的普通循环。同样正如我所说,我将映射函数类型从 更改为。这是必需的,因为您无法在其 monad 之外对可变向量(在此处作为第一个参数传递)做太多事情。但是在 monad 内部我们可以用它做任何事情,这里我们只是读取-th 元素并将其乘以.forM_
mapM_
for
(MVector (PrimState m) a -> Int -> a)
(MVector (PrimState m) a -> Int -> m a)
i
i
请注意,在此版本的函数中,您可以从映射函数内部对源向量执行任何操作。如果你不需要这个并且你只使用i
-th 元素i
,你可以进一步简化它:
imapInto' :: (PrimMonad m, MV.Unbox a, MV.Unbox b)
=> (a -> Int -> b)
-> MV.MVector (PrimState m) b
-> MV.MVector (PrimState m) a
-> m ()
imapInto' f d s = forM_ [0..MV.length s - 1] $ \i -> do
v <- MV.read s i
MV.write d i (f v i)
这里的映射函数是纯的,仅将i
-th 元素作为第一个参数,而不是向量本身。我也推广了这个版本,所以它可以将第一个向量映射到另一个类型的向量,所以源向量和目标向量不需要是相同的类型。这也是以前版本可以做的事情。