リストが長い場合は、リストの代わりにハッシュテーブルを使用して、訪問したセルを保存し、ほぼ一定の時間でルックアップを実行することをお勧めします。
Pascalのコードを拡張して変更しましょう。
let rec is_cyclic list already_visited =
match list with
Nil -> false
| Cons(h, { contents = t }) ->
V.mem already_visited h ||
is_cyclic t (V.add already_visited h)
Vモジュールは、次のファンクターアプリケーションに由来します。
module V = Visits.Make (struct type t = int end)
訪問は次のように定義されます。
(* visits.ml *)
struct
module Make (X : sig type t end) :
sig
type elt
type t
val create : int -> t
val mem : t -> elt -> bool
val add : t -> elt -> unit
end with type elt = X.t =
struct
module H = Hashtbl.Make (
struct
type t = X.t
let equal = ( == )
let hash = Hashtbl.hash
end
)
type elt = X.t
type t = unit H.t
let create len = H.create len
let mem tbl x = H.mem tbl x
let add tbl x = H.add tbl x ()
end
end
上記の実装は完全に安全で将来性がありますが、リストベースのソリューションとは異なり、ポリモーフィックではありません。
悪名高いObjモジュールを使用する多態的なバージョンを作成することは可能です。これは、公式に文書化されていない多くのことを知らずに使用するべきではありません。以下のコードでのObjの使用は、Hashtblモジュールの実装を前提としています。これは、将来破損する可能性は低いですが、警告が表示されます。
とは言うものの、それは多形であり、したがって使いやすいです:
(* visits.mli *)
type 'a t
val create : int -> 'a t
val mem : 'a t -> 'a -> bool
val add : 'a t -> 'a -> unit
(* visits.ml *)
module H = Hashtbl.Make (
struct
type t = Obj.t
(* Warning: using Obj is not pure OCaml. It makes assumptions
on the current implementation of Hashtbl,
which is unlikely to change in incompatible ways
anytime soon. *)
let equal = ( == )
let hash = Hashtbl.hash
end
)
type 'a t = unit H.t
let create len = H.create len
let mem : 'a t -> 'a -> bool = fun tbl x -> H.mem tbl (Obj.repr x)
let add : 'a t -> 'a -> unit = fun tbl x -> H.add tbl (Obj.repr x) ()