简体   繁体   English

Data.Vector,与累加器映射

[英]Data.Vector, mapping with accumulator

I would like to do a map with an accumulator on Data.Vector. 我想在Data.Vector上用累加器做一张地图。

I want to write the function inc: 我想写函数inc:

inc :: Vector.Vector Bool -> Vector.Vector Bool

which "adds one" to the vector, eg: 向载体“添加一个”,例如:

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

If there was something like Data.List's mapAccumR, say with type: 如果有类似Data.List的mapAccumR,请输入类型:

mapAccumR :: (acc -> x -> (acc, y)) -> acc -> Vector x -> (acc, Vector y)

this could be done with 这可以用

inc = snd . Vector.mapAccumR inc' True
  where
    inc' x y = (x && y, (x || y) && (not (x && y)))

but I can't figure it out how to do it with what is in Data.Vector.Unboxed. 但我无法弄清楚如何使用Data.Vector.Unboxed中的内容来做到这一点。 Is it possible? 可能吗?

The simplest solution would be to reverse your scheme and have the least significant bits on the front of the vector, like this: 最简单的解决方案是反转你的方案,并在向量的前面有最低有效位,如下所示:

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

The reason is that mapM and unfoldr are both suitable for defining inc with this bit ordering, but not with the other order, and there aren't reversed versions of these functions in vector . 原因是mapMunfoldr都适合用这个位排序定义inc ,但不适用于其他顺序,并且vector中没有这些函数的反转版本。 As an example, mapM lets us implement inc with the help of the State monad: 例如, 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)

Alternatively, we could do two reversals to get back to the original ordering. 或者,我们可以进行两次反转以恢复原始顺序。 That would be asymptotically the same, but practically significantly slower. 这将渐近相同,但实际上明显更慢。

Of course, we can still do a lower-level implementation for mapAccumR . 当然,我们仍然可以为mapAccumR做一个较低级别的实现。 This necessitates working in the ST monad with mutable vectors, which isn't particularly hard, but it's not trivial either. 这需要在具有可变载体的ST monad中工作,这不是特别困难,但它也不是微不足道的。 There isn't a lot of material online on the ST monad; ST monad网上没有很多资料; on Stack Overflow you may benefit from reading this question and optionally following the links from there. 在Stack Overflow上,您可以从阅读此问题中受益,也可以选择点击此处的链接。 I try to comment on the important parts in the mapAccumR implementation below. 我尝试评论下面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')

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM