检查 ocaml 中可变列表是否有循环?

发布于 2024-10-28 11:38:09 字数 426 浏览 0 评论 0原文

我正在尝试编写一个函数来测试 Ocaml 中的可变列表是否包含循环(即,具有对其自身的引用并连续重复。

我的列表定义为 type 'a m_list = Nil | Cons '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 ;;

但它不太正确,我不确定如何从这里继续......感谢您的帮助!

I'm trying to write a function to test whether a mutable list in Ocaml contains a cycle or not (that is, has a reference to itself and repeats continuously.

My list is defined as type 'a m_list = Nil | Cons of 'a * (('a m_list) ref).

So far, I have:

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 ;;

but it's not quite right and I'm unsure how to proceed from here...thanks for any help!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

私藏温柔 2024-11-04 11:38:09

一旦两个 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
      ...

There is a cycle in the list as soon as two Cons cells (found at different depths in the list) are the same. Your example code only checks if the first Cons cell appears again down in the list. One way to check for cycles is to remember all the Cons cells you have visited going down the list, and to compare each new cell to all the previous ones.

I'm not going to write the entire function, but it may look like this:

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
      ...
孤城病女 2024-11-04 11:38:09

Pascal Cuoq 的答案是最好的答案,但为了方便起见,您还可以使用恒定内存执行此检查(但计算成本更高),方法是保留两个游标并将其中一个游标的前进速度比另一个游标快两倍,并在它们相等时立即停止:

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  

Pascal Cuoq's answer is the best one, but for the sake of anecdotal obscurity, you can also perform this check with constant memory (but at a higher computational cost) by keeping two cursors and advancing one of them twice as fast as the other, and stopping as soon as they are equal:

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  
余生一个溪 2024-11-04 11:38:09

如果列表很长,您可能需要使用哈希表而不是列表来存储访问过的单元格并在接近恒定的时间内执行查找。

让我扩展和修改 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 定义如下:

(* 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) ()

If the list is long, you may want to use a hash table instead of a list for storing the visited cells and performing the lookups in near-constant time.

Let me expand and modify Pascal's code:

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)

The V module comes from the following functor application:

module V = Visits.Make (struct type t = int end)

and Visits is defined as follows:

(* 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

The implementation above is perfectly safe and future-proof but is not polymorphic unlike the list-based solution.

It is possible to write a polymorphic version that uses the infamous Obj module, which should not be used without knowing a number of things that are not officially documented. The use of Obj in the code below makes assumptions on the implementation of the Hashtbl module, which are unlikely to break in the future but you are being warned.

That said, it is polymorphic and therefore easy to use:

(* 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) ()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文