【问题标题】:How to generate recursive list in OCaml如何在 OCaml 中生成递归列表
【发布时间】:2013-10-18 19:16:12
【问题描述】:

我想实现 Haskell cycle 函数的模拟。

如果我明确传递列表元素,这似乎微不足道:

let cycle a b c =
  let rec l = a::b::c::l in
  l

cycle 1 2 3 生成递归列表1, 2, 3, 1...

但是,如何在另一个常规列表的基础上生成递归列表?

let cycle lst = ...

用法

cycle [1;2;3]

【问题讨论】:

    标签: recursion ocaml recursive-datastructures


    【解决方案1】:

    在 ML 这样的热切语言中,您需要使用流。例如

    # let cycle = Stream.from (fun n -> Some (List.nth [1;2;3] (n mod 3)));;
    val cycle : int Stream.t = <abstr>
    # Stream.npeek 10 cycle;;
    - : int list = [1; 2; 3; 1; 2; 3; 1; 2; 3; 1]
    

    【讨论】:

    • 是的,我知道流,但是递归列表在 OCaml 中有什么用处?
    • 不多,据我所知。我从未见过它们在实际代码中使用过。
    • 就个人而言,我不喜欢在这种情况下使用List.nth 遍历列表的想法。 cycle 变为 O(n*m) 如果您希望循环遍历 n-length 列表并查看 m 元素。据我了解cycle 应该是线性的。
    【解决方案2】:

    据我所知,OCaml 不适合这种编码,除非你想深入到语言的不安全部分。

    坚持语言的安全部分(但使用第 7 章中的扩展),这是cycle 的(不是很令人印象深刻)版本,适用于长度为 3 的列表:

    let cycle = function
        | [] -> []
        | [x] -> let rec res = x :: res in res
        | [x; y] -> let rec res = x :: q and q = y :: res in res
        | [x; y; z] -> let rec res = x :: t and t = y :: v and v = z :: res in res
        | _ -> failwith "list too long"
    

    很容易看出如何将其扩展到任何所需的固定长度,但不是任意长度。

    这是一个函数的会话:

    # #use "cyc.ml";;
    val cycle : 'a list -> 'a list = <fun>
    # cycle [1;2;3];;
    - : int list =
    [1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1;
     2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2;
     3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3;
     1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1;
     2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2;
     3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; ...]
    

    无论如何,这是我能做到的最好的。我希望它会有所帮助。

    【讨论】:

      【解决方案3】:

      似乎制作这种递归列表的唯一方法是使用Obj 模块。

      复制列表并使其递归

      let cycle lst = match lst with
        | [] -> []
        | _ ->
          let rec get_last_cell = function
            | [] -> assert false
            | _::[] as last -> last
            | _::tl -> (get_last_cell tl)
          in
          let new_lst = List.map (fun x -> x) lst in
          let last_cell = get_last_cell new_lst in
          Obj.set_field (Obj.repr last_cell) 1 (Obj.repr new_lst);
          new_lst
      

      创建递归列表,然后插入新的 cons 单元格

      let cycle lst = match lst with
        | [] -> []
        | hd::tl ->
            let rec loop cell lst =
              match lst with
              | [] -> ()
              | hd::tl ->
                  let new_cell = [hd] in
                  let new_cell_obj = Obj.repr new_cell in
                  let cell_obj = Obj.repr cell in
                  Obj.set_field new_cell_obj 1 (Obj.field cell_obj 1);
                  Obj.set_field cell_obj 1 new_cell_obj;
                  loop new_cell tl
            in
            let rec cyc_lst = hd::cyc_lst in
            loop cyc_lst tl;
            cyc_lst
      

      这个想法很简单:

      1. 创建只有一个元素的递归列表cyc_lst
      2. cyc_lst 的尾部紧接之前插入一个或多个新的 cons 单元格。

      例子

      cycle [1;2]

      1. 创建递归列表cyc_lst。它在内存中表示为一个自递归的 cons 单元

        让 rec cyc_lst = hd::cyc_lst .--------。 | | | +---+-|-+ `->| 1 | * | +---+---+
      2. 使用 2 作为唯一元素创建new_cell

        让 new_cell = [高清] 细胞新细胞 .--------。 | | | +---+-|-+ +---+---+ `->| 1 | * | | 2 | X | +---+---+ +---+---+
      3. new_cell 尾指针设置为第一个单元格

        Obj.set_field new_cell_obj 1 (Obj.field cell_obj 1) 细胞新细胞 .--------.--------------。 | | | | +---+-|-+ +---+-|-+ `->| 1 | * | | 2 | * | +---+---+ +---+---+
      4. cell尾指针设置为new_cell

        obj.set_field cell_obj 1 new_cell_obj 细胞新细胞 .------------------------。 | | | +---+---+ +---+-|-+ `->| 1 | *------->| 2 | * | +---+---+ +---+---+

      我希望 GC 可以接受这样的列表操作。如果不是,请告诉我。

      【讨论】:

        【解决方案4】:

        你也可以这样定义

        # let cycle items =
            let buf = ref [] in
            let rec next i =
              if !buf = [] then buf := items;
              match !buf with
                | h :: t -> (buf := t; Some h)
                | [] -> None in
            Stream.from next;;
        val cycle : 'a list -> 'a Stream.t = <fun>
        
        utop # let test = cycle [1; 2; 3];;
        val test : int Stream.t = <abstr> 
        utop # Stream.npeek 10 test;;
        - : int list = [1; 2; 3; 1; 2; 3; 1; 2; 3; 1]
        

        来自:

        http://ocaml.org/tutorials/streams.html

        【讨论】:

          【解决方案5】:

          您需要另一个答案中的流或惰性列表:

          type 'a llist = LNil | LCons of 'a * 'a llist Lazy.t
          let cycle = function
          | [] -> invalid_arg "cycle: empty list"
          | hd::tl ->
            let rec result =
              LCons (hd, lazy (aux tl))
            and aux = function
              | [] -> result
              | x::xs -> LCons (x, lazy (aux xs)) in
            result
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2019-10-04
            • 1970-01-01
            • 2020-03-19
            • 1970-01-01
            • 2019-04-22
            • 2017-03-26
            • 1970-01-01
            相关资源
            最近更新 更多