所以,我一直在寻找可变的哈希表(出于速度原因),而Don Stewart的updated answer让我尝试hashtables。
在ST Monad中有点缺乏经验,我成功地将given example扩展为:
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleContexts #-}
import qualified Data.HashTable.ST.Cuckoo as C
import qualified Data.HashTable.Class as H
import Control.Monad.ST.Safe
import Data.Text
type HashTable s k v = C.HashTable s k v
foo :: ST s (HashTable s Text Int)
foo = do
ht <- H.newSized 1
H.insert ht "abc" 1
H.insert ht "dea" 3
return ht
add1 :: HashTable s Text Int -> ST s (HashTable s Text Int)
add1 ht = do
H.insert ht "abc" 2
H.insert ht "dea" 4
return ht
main = do
let x = runST $ H.toList =<< foo
print x
let y = runST $ H.toList =<< add1 =<< foo
print y
从我的(有限的)理解中,它允许以可变的方式对数据结构进行操作,然后“冻结它们”并以可以通过runST
转义它的方式呈现结果 - 并且因此我们可以使用let
绑定,而不是<-
。
但是,我没有看到如何在没有始终将其转换为列表的情况下对哈希表进行操作。以下代码甚至无法编译:
-- does not compile
let z = runST foo
let w = runST $ add1 z
print w
它会出现以下错误(其中包括): hashtable.hs:31:19:
Couldn't match type `a' with `C.HashTable s Text Int'
`a' is a rigid type variable bound by
the inferred type of z :: a at hashtable01.hs:31:7
Expected type: ST s a
Actual type: ST s (HashTable s Text Int)
In the second argument of `($)', namely `foo'
In the expression: runST $ foo
In an equation for `z': z = runST $ foo
这可能是由于类型中的s
约束,我的猜测是它可能正是因为这个原因。如果我们稍后使用z
它将不具有相同的值,因为add1
在适当的位置操作,因此它不会是纯粹的。这是对的吗?
但是,在这种情况下,ST Monad仅适用于以下情况:
任何进一步的更改都需要toList / fromList有效地复制值并保持原始的imutable。在我写这篇文章的时候,我在想 - 呃,这是一个纯函数的定义,在haskell中到处使用,因此是ST Monad的用例(我是否达到了ST启蒙?)
所以,我想在这种情况下真正的问题是:这不是这个toList / fromList操作昂贵(2xO(n)),并且不可能有另一种方法来操作没有toList / fromList的副本一对功能。或者我是否过于复杂,我应该使用Hashtables的IO版本?
答案 0 :(得分:0)
你是正确的,一旦离开ST
monad,你就无法对哈希表进行操作。答案不是toList/fromList
封送,而是只有一个runST
范围超过所有你需要改变那个表。
即。正如路易斯在评论中写道的那样:&#34;把所有其他东西都拉进ST monad,直到你有一个使用哈希表作为内部实现细节的函数,并且不会将其暴露给其余的代码&#34;