同じ内容を持つ可能性がある変更可能変数は、メモリ内の別の場所に格納されているため、引き続き区別することができます。彼らは物理的な等価演算子(==
)と比較することができます。しかし、OCamlは平等よりも優れたものを提供していませんし、参照に関しては重要なハッシュ関数や順序を提供しないので、参照を格納するために構築できる唯一のデータ構造は$ \ Theta( n)ほとんどの使用のための$アクセス時間。
(汚れている場合は実際に下にあるポインタに移動できますが、ポインタは足の下で動くことがありますが、それでもなおそれを利用する方法がありますが、質問する必要がある場合は、とにかくそんなに欲求不満ではありません)。
2つの異なる参照が別個の内容を持っていれば、参照を比較するのは簡単です。だからそれを作る!あなたの参照に一意の識別子を追加してください。グローバルカウンタを保持し、参照を作成するたびに1ずつインクリメントし、カウンタ値をデータとともに格納します。これで、あなたの参照はカウンタ値によってインデックス付けされます。
let counter = ref 0
let new_var x = incr counter; ref (!counter, x)
let var_value v = snd !v
let update_var v x = v := (fst !v, x)
let hash_var v = Hashtbl.hash (fst !v)
タイプの安全性を高め、効率を向上させるには、カウンタ値とアイテムを含むデータ構造体を定義します。
let counter = ref 0
type counter = int
type 'a variable = {
key : counter;
mutable data : 'a;
}
let new_var x = incr counter; {key = !counter; data = x}
let hash_var v = Hashtbl.hash v.key
あなたはモジュールに上記のコードを入れて、counter
タイプの要約を行うことができます。また、ファンクションインタフェースHashtbl
を使用してハッシュテーブルモジュールを定義することもできます。よりクリーンでモジュラーな構造で変数とハッシュテーブル構造を定義する別の方法があります。
module Counter = (struct
type t = int
let counter = ref 0
let next() = incr counter; !counter
let value c = c
end : sig
type t
val next : unit -> t
val value : t -> int
end)
module Variable = struct
type 'a variable = {
mutable data : 'a;
key : Counter.t;
}
let make x = {key = Counter.next(); data = x}
let update v x = v.data <- x
let get v = v.data
let equal v1 v2 = v1 == v2
let hash v = Counter.value v.key
let compare v1 v2 = Counter.value v2.key - Counter.value v1.key
end
module Make = functor(A : sig type t end) -> struct
module M = struct
type t = A.t Variable.variable
include Variable
end
module Hashtbl = Hashtbl.Make(M)
module Set = Set.Make(M)
module Map = Map.Make(M)
end
タイプvariable
パラメトリックと標準ライブラリデータ構造ファンクタ(Hashtbl.Make
、Set.Make
、Map.Make
が)のみなし引数で型コンストラクタのために定義されているので、我々は中間モジュールVariable
を必要とします。ここでは、ポリモフィックインターフェイス(関連する関数はあるがデータ構造はない)と、関連するハッシュテーブル(およびセットとマップ)タイプを持つ任意の数の単相性のインスタンスを作成するファンクタを公開するインターフェイスを示します。あなたはあなたのプログラムが実行中に以上2^30変数を生成することが予想される場合には、int
はそれをカットしないこと
module Variable : sig
type 'a variable
val make : 'a -> 'a variable
val update : 'a variable -> 'a -> unit
val get : 'a variable -> 'a
val equal : 'a -> 'a -> bool
val hash : 'a variable -> int
val compare : 'a variable -> 'b variable -> int
end
module Make : functor(A : sig type t end) -> sig
module M : sig
type t = A.t variable.variable
val make : A.t -> t
val update : t -> A.t -> unit
val get : t -> A.t
val equal : t -> t -> bool
val hash : t -> int
val compare : t -> t -> int
end
module Hashtbl : Hashtbl.S with type key = M.t
module Set : Set.S with type key = M.t
module Map : Map.S with type key = M.t
end
は注意してください。 2^60ビットの値を作成するには、2つのint
値、またはInt64.t
が必要です。
プログラムがマルチスレッドの場合、インクリメントと参照をアトミックにするには、カウンタの周りにロックが必要であることに注意してください。
Hashtableの問題ではない:「[1; 2]」をキーとして使用すると、キー「[5; 1; 2]」であなたの価値を見つけるのはなぜですか?それは奇妙な呼称言語でしか動作しません。 – lambdapower
@lambdapower、彼は[1; 2]を使用していない、彼は身元を持っている参照を使用しています。意味的には、それはアイデンティティによって鍵を付けることに完全に意味をなさない。実装にはコストがかかります。しかし、いくつかの言語はそのコストを支払う。 –