【问题标题】:STArray documentation for newbies and State/ST related questions用于新手和州/ST 相关问题的 STArray 文档
【发布时间】:2011-11-19 20:26:31
【问题描述】:

我很难从我通过 Google 找到的文档和其他方法/讨论中理解 STArray。我在下面有一些更相关的问题。

根据文档,STArrays 是

ST monad 中的可变装箱和未装箱数组。

这给我的印象是,STArray 旨在用作在函数之间传递的状态(假设您有一个必须经常更新的向量)。

显然这是不同的用法:

ST s (STArray s a e)

s 这里的状态是什么?如果是内部使用,那为什么不对用户隐藏呢?

这也意味着,如果我们想使用 STArray s Int Int 作为状态传递,可以定义

type StateArray a = Control.Monad.State (ST s (STArray s Int Int)) a

这看起来相当麻烦。

最后,

  • STState 有什么区别?
  • 如果STIO 用于“内部”使用,STArrayIOArray 之间有什么区别?

谢谢!!

【问题讨论】:

  • 如果你想看看向量,通常比数组更容易使用。

标签: arrays haskell state higher-rank-types


【解决方案1】:

ST 是一个单子,其中允许有限类型的副作用,即可变引用和可变数组。因此,它允许您实现从外部世界看是纯粹的函数,但在内部使用突变。

这与State 不同,后者仅通过将状态作为额外输入和输出通过计算线程化来伪造突变。在实现一些命令式算法时,区别很重要,因为它们有时需要突变才能有效地实现。例如,在 State monad 中使用常规数组,您只能通过复制来修改它,而使用 ST 您可以就地进行真正的突变。

我们之所以同时拥有STIO是因为ST提供了比IO更强的保证,即:

  1. ST 不允许任意副作用,例如访问文件系统。
  2. 我们可以保证ST确实允许的副作用不会逃出runST的范围,所以在外界看来是纯粹的。

之所以能保证副作用不能逃逸,与类型变量s有关。由于任何 ST 操作在 s 中必须是多态的,因此您不能编写允许任何可变引用进入或离开 runST 范围的代码,因为类型检查器会抱怨它不能保证您的 s动作和引用或数组的动作是相同的除非它们来自同一个runST 范围。

作为使用带有可变数组的 ST monad 的示例,这里是 Erathostenes 筛的实现:

import Control.Monad
import Control.Monad.ST
import Data.Array.ST
import Data.Array.Unboxed

primesUpto :: Int -> [Int]
primesUpto n = [p | (p, True) <- assocs $ sieve n]

sieve :: Int -> UArray Int Bool
sieve n = runSTUArray $ do
    sieve <- newArray (2, n) True
    forM_ [2..n] $ \p -> do
        isPrime <- readArray sieve p
        when isPrime $ do
            forM_ [p*2, p*3 .. n] $ \k -> do
                writeArray sieve k False
    return sieve

runSTUArrayrunST 的一种特殊形式,它允许您在内部使用突变构建一个数组,然后将其冻结并将其作为不可变数组返回。 newArrayreadArraywriteArray 做你想做的事。

如您所见,sieve 的类型签名表明它是一个纯函数,而且确实如此。但是,它在内部大量使用变异来有效地实现它。

【讨论】:

  • 感谢您的精彩解释!
  • 是否有相当于 runSTUarray 的向量?
猜你喜欢
  • 1970-01-01
  • 2021-06-23
  • 1970-01-01
  • 1970-01-01
  • 2011-02-01
  • 2011-02-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多