面向新手的 STArray 文档和状态/ST 相关问题

发布于 2024-12-16 17:10:08 字数 739 浏览 0 评论 0原文

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

根据文档,STArray

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 之间有什么区别?

谢谢你!!

I have a hard time to understand STArray from the documentation and other howtos/discussion I've found through Google. I've got some more related questions below.

According to the documentation, STArrays are

Mutable boxed and unboxed arrays in the ST monad.

This gave me the impression, that STArray is meant to be used as a state being passed around between functions (imagine you have a vector that has to be updated often).

Apparently this is used differently though:

ST s (STArray s a e)

What is the state s here? If it is used internally, then why is this not hidden from the user?

This also means, if we want to use a STArray s Int Int being passed around as state, one would define

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

which seems rather cumbersome.

Finally,

  • what is the difference between ST and State?
  • what is the difference between STArray and IOArray, if the ST and IO are meant for "internal" use?

Thank you!!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

情愿 2024-12-23 17:10:08

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

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

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

  1. ST 不允许任意副作用,例如访问文件系统。
  2. 我们可以保证 ST does 允许的副作用无法逃脱 runST 的作用域,因此从外界来看它可以被视为纯粹的。

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

作为将 ST monad 与可变数组结合使用的示例,以下是埃拉托斯坦筛法的实现:

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< 的特殊形式/code> 它允许您在冻结数组并将其作为不可变数组返回之前使用内部突变来构建数组。 newArrayreadArraywriteArray 执行您所期望的操作。

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

ST is a monad in which a limited type of side effects are allowed, namely mutable references and mutable arrays. Thus it allows you to implement functions which are pure as seen from the outside world, but which use mutation internally.

This is different from State, which only fakes mutation by threading the state through your computation as extra inputs and outputs. The difference is important when implementing some imperative algorithms, because they sometimes need mutation to be implemented efficiently. For example using a regular array in a State monad, you can only modify it by making a copy, whereas with ST you can have true mutation in-place.

The reason why we have both ST and IO is that ST provides stronger guarantees than IO, namely:

  1. ST does not allow arbitrary side effects like for example accessing the file system.
  2. We can guarantee that the side effects ST does allow cannot escape the scope of runST, and so it can be seen as pure from the outside world.

The reason why we can guarantee that the side effects cannot escape is related to the type variable s. Since any ST action must be polymorphic in s, you cannot write code which allows any mutable references to enter or leave the scope of a runST, because the type checker will complain that it cannot guarantee that the s of your action and that of the reference or array are the same unless they come from the same runST scope.

As an example of using the ST monad with mutable arrays, here is an implementation of the Sieve of 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

runSTUArray is a specialized form of runST which allows you to build an array using mutation on the inside, before freezing it and returning it as an immutable array. newArray, readArray and writeArray do what you'd expect.

As you can see, the type signature of sieve indicates that it's a pure function, and it is. However, it uses mutation heavily on the inside to implement it efficiently.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文