【问题标题】:removing cycles from cyclic/mutable list in ocaml?从 ocaml 的循环/可变列表中删除循环?
【发布时间】:2011-07-27 17:09:02
【问题描述】:

我不确定如何从可变类型列表中删除循环:

type 'a m_list = Nil | Cons of 'a * (('a m_list) ref)

例如如果我有一个列表 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 是否有循环。我想以破坏性方式(更新参考)或非破坏性方式(创建新列表)来执行此操作。

谢谢!

【问题讨论】:

    标签: ocaml mutable cyclic


    【解决方案1】:

    基于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 替换为另一个数据结构。

    【讨论】:

      【解决方案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)
      

      【讨论】:

        猜你喜欢
        • 2011-07-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-28
        • 1970-01-01
        • 1970-01-01
        • 2014-08-26
        相关资源
        最近更新 更多