我想在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中的内容来做到这一点.可能吗?
最佳答案 最简单的解决方案是反转你的方案,并在向量的前面有最低有效位,如下所示:
inc <False, False, False> == <True, False, False>
原因是mapM
和unfoldr
都适用于使用此位排序定义inc,但不适用于其他顺序,并且在向量中没有这些函数的反转版本.例如,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')