3

这与这个问题有关:

不知道从哪里开始使用可变向量

我将如何编写一个从一个向量中获取值、转换它们并将结果放入第二个向量的函数?更具体地说,它应该遍历源数组的所有索引,并将该索引和数组交给一个函数,然后将函数的结果存储在另一个数组中。

我认为签名看起来像这样:

imapInto :: (PrimMonad m, Unbox a) => (MVector (PrimState m) a -> Int -> a) -> MVector (PrimState m) a -> MVector (PrimState m) a  -> m ()

并且会被称为这样的东西:

import Data.Vector.Unboxed.Mutable MV

...
let dst = MV.replicate 10 0.0 in
let src = MV.replicate 10 1.0 in
imapInto (\src' i -> (src' * 2.0)) dst src
...

这会将 src 中的所有元素乘以 2 并将结果放入 dst,从而产生一个状态单元。

4

2 回答 2

5

我认为没有内置函数可以满足您的需求。但是使用这些原语应该可以为您的任务编写命令式代码。如果它足够通用,你甚至应该可以在纯代码中使用它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)ii

请注意,在此版本的函数中,您可以从映射函数内部对源向量执行任何操作。如果你不需要这个并且你只使用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 元素作为第一个参数,而不是向量本身。我也推广了这个版本,所以它可以将第一个向量映射到另一个类型的向量,所以源向量和目标向量不需要是相同的类型。这也是以前版本可以做的事情。

于 2012-07-18T20:05:57.857 回答
1

对于您的示例,您根本不需要可变向量,您可以使用mapgenerate沿以下未经测试的代码行:

import Data.Vector.Unboxed as V
import Data.Vector.Unboxed.Mutable as M

double :: V.Vector Int -> V.Vector Int
double = V.map (*2)

如果你想使用索引:

addIndex = V.imap (\element idx -> element + idx)

在构造一个不可变向量后,您可以使用unsafeThaw(假设您知道没有其他对该向量的引用)来获得MVectorO(1) 中的值。所以你最终的“imapInto”可能看起来像:

imapInto :: some constraints => (V.Vector a -> Int -> b) -> V.Vector a -> M.Vector b
imapInto op v = V.unsafeThaw (V.imap op v)

如果您真的希望在调用之前imapInto而不是在调用期间分配结果向量,请参阅 Vladimir 的回答。

于 2012-07-19T02:56:32.820 回答