Haskell には、次のようなコンテナがあります。
data Container a = Container { length :: Int, buffer :: Unboxed.Vector (Int,a) }
このコンテナは平坦化されたツリーです。そのアクセサは、が格納されている適切なバケットを見つけるために、ベクトルを介して(!)
バイナリ ( ) 検索を実行します。log(N)
index
(!) :: Container a -> Int -> a
container ! index = ... binary search ...
連続したアクセスは同じバケットにある可能性が高いため、これは次の方法で最適化できます。
if `index` is on the the last accessed bucket, skip the search
難点はそのlast accessed bucket
部分です。JavaScript では、コンテナ オブジェクトの隠し変数を不純に変更するだけです。
function read(index,object){
var lastBucket = object.__lastBucket;
// if the last bucket contains index, no need to search
if (contains(object, lastBucket, index))
var bucket = lastBucket;
// if it doesn't
else {
// then we search the bucket
var bucket = searchBucket(index,object);
// And impurely annotate it on the container, so the
// next time we access it we could skip the search.
container.__lastBucket = bucket;
}
return object.buffer[bucket].value;
}
これは単なる最適化であり、実行された分岐に関係なく結果は同じであるため、参照透過性が損なわれることはないと思います。Haskell で、ランタイム値に関連付けられた状態を純粋に変更できないのはどうしてですか?
〜
私は2つの可能な解決策を考えました。
ポインターを値にリンクするグローバルで変更可能なハッシュマップ
lastBucket
であり、unsafePerformIO を使用してそれに書き込みます。しかし、オブジェクトのランタイム ポインタ、または少なくとも何らかの一意の ID を取得する方法が必要です (方法は?)。、 に追加のフィールドを追加し、
Container
何らかのlastBucket :: Int
方法で 内(!)
で純粋に変更せず、そのフィールドを内部と見なします (明らかに参照透過性を壊すため)。