【问题标题】:A monad for piecewise mutable state分段可变状态的单子
【发布时间】:2018-09-05 15:43:01
【问题描述】:

我知道普通的State 是如何工作的(编辑:显然不是!)。

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

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

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

我不想诉诸 IOArrays 或 IO monad 中的任何东西。我有哪些选择?

【问题讨论】:

  • 为什么不能只使用STArrays?
  • @leftaroundabout 假设我想修改 array1 中的一个单元格,该单元格是根据 array2 中的单元格计算的。然后修改 array2 中的一个单元格,根据 array1 中的单元格计算。重复直到完成。这可能只用两个 STArrays 吗?我没有办法……
  • @leftaroundabout 嗯,我想这可以用 monad 转换器来完成。堆叠这两种状态并根据需要提升。这是正确的吗?
  • @n.m.为什么要使用单子变压器?为什么不把它们都放在ST? (好吧,由于量词,我认为它不适用于 runSTArray,但我觉得应该有办法做到这一点 - 直接使用 unsafeFreeze ?)
  • @Cubic 不知何故,我的印象是你只能在 runST 中拥有一个状态。完全错误。

标签: haskell state-monad


【解决方案1】:

这里有一些选项。

{-# 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)

【讨论】:

  • 天哪,我现在觉得自己好蠢。我怎么没看到这个。谢谢。
猜你喜欢
  • 2020-11-12
  • 2022-01-10
  • 2011-04-28
  • 2014-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-06
相关资源
最近更新 更多