我不确定如何从类型的可变列表中删除循环:
type 'a m_list = Nil | Cons of 'a * (('a m_list) ref)
E.g。如果我有一个清单3,2,2,1,2,1,2,1,.....我想得到一个3,2,2,1。
我无法弄清楚的是初始循环的位置 - 我有一个看起来像这样的递归,但我无法弄清楚如何将它包装成递归函数;显然,这里只会检查前几个术语。
let remove list : unit =
if is_cyclic list then match list with
|Nil->()
|Cons(_,v)-> match (!v) with
|Nil->()
|Cons(_,x)->match (!x) with
|Nil->()
|Cons(_,y)->match (!y) with
|Nil->()
|Cons(_,p) -> if is_cyclic (!p) then p:=Nil else ()
我有一个is_cyclic函数,告诉我m_list是否有循环。我想要破坏性地(更新参考)或者非破坏性地(创建一个新的列表)这样做。
谢谢!
答案 0 :(得分:3)
根据前一个问题的Pascal Cuoq's answer,你可以尝试这样的事情:
let rec recurse list already_visited =
match list with
Nil -> ()
| Cons(h, t) ->
if List.memq !t already_visited
then t := Nil
else recurse !t (t :: already_visited)
let remove_cycles list = recurse list []
这将遍历列表,直到它到达结尾或访问元素两次。当后者发生时,它会将最后一次访问的引用设置为Nil
。
如果列表非常大,您可能希望将already_visited
替换为其他数据结构。
答案 1 :(得分:2)
如果没有足够的内存来存储每个先前访问过的元素,您可以使用循环检测算法在循环中查找元素,然后使用它,找到循环的结束并覆盖它的下一个引用。
要执行此操作,请修改is_cyclic
以返回'a mlist ref
而不是bool
。假设它可能在循环中间返回一个元素,运行原始列表并检查每个元素是否在循环中。这将为您提供循环中的第一个元素。
从那里很容易找到周期的结束 - 只需循环直到你回到开头。
这样的事情:
let rec in_cycle x st cyc =
if cyc == x then true
else
match !cyc with Nil -> false
| Cons(_, t) when t == st -> false
| Cons(_, t) -> in_cycle x st t
let rec find_start l cyc =
if in_cycle l cyc cyc then l
else
match !l with Nil -> raise Not_found
| Cons(_, t) -> find_start t cyc
let rec find_end st cyc =
match !cyc with Nil -> raise Not_found
| Cons(_, t) ->
if t == st then cyc
else find_end st t
(* ... *)
let cyc = is_cyclic list in
let st = find_start list cyc in
let e = (find_end st cyc) in
match !e with Nil -> failwith "Error"
| Cons(v, _) -> e := Cons(v, ref Nil)