我正在尝试编写一个函数来测试Ocaml中的可变列表是否包含一个循环(即,有一个自身的引用并连续重复。
我的列表定义为type 'a m_list = Nil | Cons of 'a * (('a m_list) ref)
。
到目前为止,我有:
let is_cyclic list =
let rec check xs =
match (!xs) with
|Nil -> false
|Cons(_,v) -> ((!v)==list)||check v in
match list with
|Nil -> false
|Cons(_, x) -> check x ;;
但这不太对,我不确定如何从这里开始......感谢您的帮助!
答案 0 :(得分:3)
只要两个Cons单元格(在列表中的不同深度处找到)相同,列表中就会有一个循环。您的示例代码仅检查第一个Cons单元格是否再次出现在列表中。检查周期的一种方法是记住您在列表中访问过的所有Cons单元格,并将每个新单元格与之前的所有单元格进行比较。
我不会写整个函数,但它可能看起来像这样:
let rec is_cyclic list already_visited =
match list with
Nil -> false
| Cons(h, { contents = t }) ->
if List.memq list already_visited
then (* list was traversed before *)
...
else
...
答案 1 :(得分:2)
Pascal Cuoq's answer是最好的,但是为了轶事默默无闻,你也可以通过保持两个光标并以两倍快的速度推进其中一个光标,以恒定的内存(但计算成本更高)执行此检查作为另一个,并在它们平等时立即停止:
let rec is_cyclic a b =
match a with
| Nil -> false
| Cons (_, { contents = a }) ->
match b with
| Nil | Cons (_, { contents = Nil }) -> false
| Cons (_, { contents = Cons (_, {contents = b}) } ->
a == b || is_cyclic a b
let is_cyclic l = is_cyclic l l
答案 2 :(得分:0)
如果列表很长,您可能希望使用哈希表而不是列表来存储访问的单元格,并在接近恒定的时间内执行查找。
让我展开并修改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) ()