使用和返回多个STUArrays

时间:2014-02-20 17:27:29

标签: arrays haskell st-monad

我一直在研究如何在ST计算中创建和使用多个STUArrays。具体方案是:

  1. 创建多个数组,但只返回其中一个
  2. 创建多个数组但不返回任何数组
  3. 创建多个数组并返回多个数组
  4. 我有(1)和(2)的答案,但没有答案(3)。

    首先是一些进口,所以我们知道一切都来自哪里:

    import Control.Monad.ST (ST,runST)
    import Data.Array.Base (unsafeFreezeSTUArray)
    import Data.Array.ST (STUArray)
    import Data.Array.Unboxed (UArray)
    import Data.STRef (STRef, newSTRef, readSTRef, writeSTRef)
    import Data.Array.MArray (getBounds, newArray, readArray, writeArray, newListArray)
    import Data.Array.ST (runSTUArray)
    

    对于(1),一个技巧是定义一个新的数据类型和构造函数:

    data ArrayPair s = AP (STUArray s Int Int) (STUArray s Int Bool)
    
    newAP n = do
      a1 <- newArray (1,n) 0
      a2 <- newArray (1,n) False
      return $ AP a1 a2
    

    以下是如何返回其中一个数组:

    foo :: UArray Int Int
    foo = runSTUArray $ do
            AP ints bools <- newAP 10
            writeArray ints 1 42
            writeArray bools 1 True
            -- do stuff with ints and bools
            return ints
    

    对于(2),您可以向数据结构添加STRef,使用readSTRef结束计算并使用runST运行:

    data WorkState s = WS (STUArray s Int Int)
                          (STUArray s Int Bool)
                          (STRef s Char)
    
    newWS = do
      ints <- newArray (1,10) 0
      bools <- newArray (1,20) False
      char <- newSTRef 'X'
      return $ WS ints bools char
    
    bar :: Char
    bar = runST $ do
      WS ints bools char <- newWS
      writeArray ints 3 36
      writeArray bools 5 True
      writeSTRef char 'Z'
      -- ...
      readSTRef char
    

    对案例(1)和(2)的这种方法的评论?情况(3)呢?

    baz :: (UArray Int Int, UArray Int Bool)
    baz = runST??? $ do
      AP ints bools <- newAP
      ...
      return (ints,bools)  -- ???
    

1 个答案:

答案 0 :(得分:3)

您有两个选择:

  1. 使用freeze。这需要复制数组,但您不需要使用任何不安全的函数:

    baz :: (UArray Int Int, UArray Int Bool)
    baz = runST $ do
      AP ints bools <- newAP 12
      liftM2 (,) (freeze ints) (freeze bools)
    
  2. 使用runSTUArray为两个数组创建unsafeFreezeSTUArray变体,知道实现实际上是安全的(因为没有对原始可变数组的引用)。

    runSTUArray2 :: (Ix i1, Ix i2)
               => (forall s . (ST s (STUArray s i1 e1, STUArray s i2 e2)))
               -> (UArray i1 e1, UArray i2 e2)
    runSTUArray2 st = runST $ do
        (a1, a2) <- st
        liftM2 (,) (unsafeFreezeSTUArray a1) (unsafeFreezeSTUArray a2)
    
    baz' :: (UArray Int Int, UArray Int Bool)
    baz' = runSTUArray2 $ do
      AP ints bools <- newAP 12
      return (ints, bools)
    

    (也许这种方法甚至可以使用泛型以某种方式推广,以允许返回包含ST数组的任何数据结构。)