haskell - 分段可变状态的单子(monad)

标签 haskell state-monad

我知道多么平凡State有效( 编辑:显然不是!)。

如果我需要创建一个数组,并且一次创建整个数组不方便,我可以创建一个 STArray ,填充它,然后卡住并返回一个正常的不可变数组给用户。

现在假设我需要同时创建两个不同类型的数组。

更一般地说,我可能想创建一个带有可变节点的任意图,然后逐个节点修改它一段时间,就像我修改 STArray 一样。一个单元一个单元,然后立即卡住整个图形并返回正常的不可变数据。

我不想诉诸 IOArray s 或 IO 中的任何内容单子(monad)。我有哪些选择?

最佳答案

这里有一些选项。

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
module TestSTArray where

import Control.Monad.ST
import Data.Array.ST
import Data.Array
import Data.Array.MArray

-- not needed until later on    
import GHC.Arr (unsafeFreezeSTArray)

基本的、安全的方法是冷冻。但是,这将导致复制。
test2Safe :: (Array Int Char, Array Int Bool)
test2Safe = runST $ do
   a1 <- newArray (0,9) 'A' :: ST s (STArray s Int Char)
   a2 <- newArray (0,9) False :: ST s (STArray s Int Bool)
   writeArray a1 5 'B'
   x <- readArray a2 6
   writeArray a1 7 (if x then 'X' else 'Y')
   writeArray a2 5 True
   arr1 <- freeze a1
   arr2 <- freeze a2
   return (arr1, arr2)

风险更大,但可能仍然安全的是利用低杠杆/不安全的 GHC 原语并构建安全 runSTArray 的扩展变体.这样我们就避免了复制。
runSTArray2 :: (forall s. ST s (STArray s i1 e1, STArray s i2 e2))
            -> (Array i1 e1, Array i2 e2) 
runSTArray2 st = runST $ do
  (a1, a2) <- st
  (,) <$> unsafeFreezeSTArray a1 <*> unsafeFreezeSTArray a2

我相信上面使用unsafe东西实际上是安全的,因为我们不再使用 a1,a2在不安全卡住之后,因此不需要副本。

当然,上面的包装器可以推广到更多的数组。可以说,应该将更通用的版本放入库中。

最后,我们可以利用辅助功能:
test2LessSafe :: (Array Int Char, Array Int Bool)
test2LessSafe = runSTArray2 $ do
   a1 <- newArray (0,9) 'A' :: ST s (STArray s Int Char)
   a2 <- newArray (0,9) False :: ST s (STArray s Int Bool)
   writeArray a1 5 'B'
   x <- readArray a2 6
   writeArray a1 7 (if x then 'X' else 'Y')
   writeArray a2 5 True
   return (a1, a2)

关于haskell - 分段可变状态的单子(monad),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52189019/

相关文章:

scala - 使用状态 monad 遍历时如何处理嵌套结构

haskell - 再来一次……我可以举一个状态单子(monad)的例子吗?

haskell - 我重写的 foldl 函数优化了吗?

haskell - 如何在 Haskell 中优化软实时应用程序的垃圾收集?

Haskell 折叠嵌套列表

haskell - 使用秒差距将文本删除到特殊字符

haskell - 如何在 Haskell 中以隐藏的方式初始化状态(就像 PRNG 一样)?

haskell - 使用 let 和 show 在 haskell 中进行列表理解,它有什么用?

haskell - 如何缩放 monad 转换器?

haskell - get 和 put 函数的作用是什么?