我想在Data.Vector上用累加器做一张地图。
我想编写函数inc:
inc :: Vector.Vector Bool -> Vector.Vector Bool
向载体“添加一个”,例如:
inc <False, False, False> = <False, False, True>
inc <False, False, True> = <False, True, False>
inc <True, True, True> = <False, False, False>
如果有类似Data.List的mapAccumR,请输入类型:
mapAccumR :: (acc -> x -> (acc, y)) -> acc -> Vector x -> (acc, Vector y)
这可以用
完成inc = snd . Vector.mapAccumR inc' True
where
inc' x y = (x && y, (x || y) && (not (x && y)))
但是我无法弄明白如何使用Data.Vector.Unboxed中的内容来完成它。有可能吗?
答案 0 :(得分:2)
最简单的解决方案是反转你的方案,并在向量的前面有最低有效位,如下所示:
inc <False, False, False> == <True, False, False>
原因是mapM
和unfoldr
都适合用这个位排序来定义inc
,但不适用于其他顺序,并且没有相反的版本vector
中的这些功能。例如,mapM
允许我们在State monad的帮助下实现inc
:
import Control.Monad.State
import qualified Data.Vector.Unboxed as V
inc :: V.Vector Bool -> V.Vector Bool
inc v = evalState (V.mapM go v) True where
go acc = state $ \x -> (x /= acc, x && acc)
或者,我们可以进行两次反转以恢复原始顺序。这将渐近相同,但实际上明显更慢。
当然,我们仍然可以为mapAccumR
执行较低级别的实施。这需要在ST
monad中使用可变向量进行工作,这不是特别困难,但它也不是微不足道的。 ST monad上没有很多在线资料;在Stack Overflow上你可以benefit from reading this question并可选择跟随那里的链接。我尝试评论下面mapAccumR
实施中的重要部分。
-- we need this so we can annotate objects in the ST monad with
-- the right parameters
{-# LANGUAGE ScopedTypeVariables #-}
import Control.Monad.ST.Strict
import qualified Data.Vector.Unboxed as V
import qualified Data.Vector.Unboxed.Mutable as MV
-- note that I explicitly introduce the type variables
-- with forall. This - in conjunction with ScopedTypeVariables -
-- lets us refer to the type variables in the function body.
mapAccumR ::
forall x y acc.
(V.Unbox x, V.Unbox y) =>
(acc -> x -> (acc, y)) -> acc -> V.Vector x -> (acc, V.Vector y)
mapAccumR f acc v = runST $ do
let len = V.length v
-- Allocate a mutable unboxed vector of v's size.
-- We need to annotate the "s" parameter here, so we can
-- refer to it in the type of "go".
(mv :: MV.STVector s y) <- MV.unsafeNew len
-- iterate through the new vector in reverse order,
-- updating the elements according to mapAccumR's logic.
let go :: Int -> acc -> ST s acc
go i acc | i < 0 = return acc
go i acc = do
-- y comes from the old vector
-- we can access it via the immutable API
let (acc' , y) = f acc (V.unsafeIndex v i)
-- but we must do mutable writes on the new vector
MV.unsafeWrite mv i y
go (i - 1) acc'
acc' <- go (len - 1) acc
-- "unsafeFreeze" converts the mutable vector to
-- an immutable one in-place.
v' <- V.unsafeFreeze mv
return (acc', v')