【问题标题】:Data.Vector, mapping with accumulatorData.Vector,用累加器映射
【发布时间】:2015-03-07 11:47:37
【问题描述】:

我想在 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 这样的东西,用 type 说:

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 中的内容。有可能吗?

【问题讨论】:

    标签: haskell


    【解决方案1】:

    最简单的解决方案是颠倒您的方案并在向量的前面添加最低有效位,如下所示:

    inc <False, False, False> == <True, False, False>
    

    原因是mapMunfoldr 都适合以这种位顺序定义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')
    

    【讨论】:

    • 谢谢!我觉得它应该可以与 monadic 的东西一起使用,但无法将它拼凑在一起,我必须阅读 state monad。
    • 如果你真的想做一个通用的包,你可能应该对流包搞砸。
    • 没错,但我不熟悉写流融合代码。我相信,至少mapM 解决方案是流式传输的。顺便说一句,最新的 vector 没有流包。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-06-10
    • 1970-01-01
    • 1970-01-01
    • 2014-07-12
    • 1970-01-01
    • 1970-01-01
    • 2019-12-18
    相关资源
    最近更新 更多