haskell – Data.Vector,与累加器映射

我想在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>

原因是mapMunfoldr都适用于使用此位排序定义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')
点赞