それで、私は(速度上の理由から)変更可能なハッシュテーブルを探していました.Don Stewartからの更新された回答により、 hashtablesを試してみました.
STモナドに少し慣れていないので、与えられた例を次のように拡張することに成功しました:
{-# 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 モナドは、次のような状況でのみ役立ちます。
- 固定入力を与える
- 可変データ構造を使用して、それに基づいて新しい値を計算します
- 結果を凍結して、再び不変にします。
それ以上の変更には、値を効果的にコピーし、元の不変を保持する toList/fromList が必要です。私がこれを書いているとき、私は考えています - 当然、これは純粋な関数の定義であり、haskell のあらゆる場所で使用されているため、ST モナドのユースケースです (私は ST 啓蒙に達しましたか?)
したがって、この場合の本当の問題は、この toList/fromList 操作は高価 (2xO(n)) ではないか、関数の toList/fromList ペアを使用せずにコピーを操作する別の方法がないかということです。 . それとも、複雑すぎて、Hashtables の IO バージョンを使用する必要がありますか?