【问题标题】:Combine Lists with Same Heads in a 2D List (OCaml)在 2D 列表中合并具有相同头部的列表 (OCaml)
【发布时间】:2011-01-17 02:08:04
【问题描述】:

我正在使用 OCaml 中的列表列表,并且我正在尝试编写一个函数来组合所有共享相同头部的列表。这是我到目前为止所拥有的,我使用了 List.hd 内置函数,但毫不奇怪,我得到了失败的“hd”错误:

let rec combineSameHead list nlist = match list with
 | [] -> []@nlist
 | h::t -> if List.hd h = List.hd (List.hd t)
    then combineSameHead t nlist@uniq(h@(List.hd t))
    else combineSameHead t nlist@h;;

例如,如果我有这个列表:

[[Sentence; Quiet]; [Sentence; Grunt]; [Sentence; Shout]]

我想把它组合成:

[[Sentence; Quiet; Grunt; Shout]]

我编写的函数 uniq 只是删除了列表中的所有重复项。请让我知道我将如何完成这项工作。提前致谢!

【问题讨论】:

    标签: ocaml


    【解决方案1】:

    一方面,我通常避免使用List.hd 之类的函数,因为模式加工通常更清晰且不易出错。在这种情况下,您的 if 可以替换为受保护的模式(模式后的 when 子句)。我认为导致您的错误的原因是您的代码在t[] 时失败;受保护的模式通过使案例更明确来帮助避免这种情况。因此,您可以将(x::xs)::(y::ys)::t when x = y 作为match 表达式中的子句来检查列表的前两个元素的头部是否相同。在 OCaml 中,有几个连续的模式并不少见,这些模式除了警卫之外是相同的。

    进一步的事情:你不需要[]@nlist - 就像写nlist一样。

    此外,您的 nlist@h 和类似表达式似乎在将列表传递给递归调用之前尝试连接它们;然而,在 OCaml 中,函数应用程序比任何运算符绑定得更紧密,因此它实际上将递归调用的结果附加到 h

    我暂时没有该函数的正确版本。但我会先用受保护的模式编写它,然后看看它能让你在多大程度上解决它。

    【讨论】:

      【解决方案2】:

      您的预期操作有一个简单的递归描述:递归处理列表的尾部,然后对头部执行“插入”操作,查找以相同头部开头的列表,如果找到,插入所有元素,但头部,否则将其附加在末尾。然后,您可以反转结果以获取您想要的列表列表。

      在 OCaml 中,该算法如下所示:

      let process list = 
        let rec insert (head,tail) = function
          | [] -> head :: tail 
          | h :: t -> 
            match h with 
            | hh :: tt when hh = head -> (hh :: (tail @ t)) :: t 
            | _ -> h :: insert (head,tail) t
        in
        let rec aux = function 
          | [] -> []
          | [] :: t -> aux t
          | (head :: tail) :: t -> insert (head,tail) (aux t) 
        in
        List.rev (aux list)
      

      【讨论】:

        【解决方案3】:

        考虑使用Map 或哈希表来跟踪磁头和为每个磁头找到的元素。如果具有相同头部的列表不相邻,则nlist 辅助列表不是很有帮助,如下例所示:

        # combineSameHead [["A"; "a0"; "a1"]; ["B"; "b0"]; ["A"; "a2"]]
        - : list (list string) = [["A"; "a0"; "a1"; "a2"]; ["B"; "b0"]]
        

        【讨论】:

          【解决方案4】:

          我可能会按照安东纳科斯的建议做一些事情。它将完全避免在列表中搜索的 O(n) 成本。您可能还会发现使用StringSet.t StringMap.t 更容易进行进一步处理。当然,可读性是最重要的,我仍然认为符合该标准。

          module OrderedString =
              struct
                  type t = string
                  let compare = Pervasives.compare
              end
          
          module StringMap = Map.Make (OrderedString)
          module StringSet = Set.Make (OrderedString)
          
          let merge_same_heads lsts =
              let add_single map = function
                  | hd::tl when StringMap.mem hd map ->
                      let set = StringMap.find hd map in
                      let set = List.fold_right StringSet.add tl set in
                      StringMap.add hd set map
                  | hd::tl ->
                      let set = List.fold_right StringSet.add tl StringSet.empty in
                      StringMap.add hd set map
                  | []     ->
                      map
              in
              let map = List.fold_left add_single StringMap.empty lsts in
              StringMap.fold (fun k v acc-> (k::(StringSet.elements v))::acc) map []
          

          【讨论】:

            【解决方案5】:

            您可以使用标准库做很多事情:

            (* compares the head of a list to a supplied value.  Used to partition a lists of lists *)
            let partPred x = function h::_ -> h = x
              | _ -> false
            
            let rec combineHeads = function [] -> []
              | []::t -> combineHeads t (* skip empty lists *)
              | (hh::_ as h)::t -> let r, l = List.partition (partPred hh) t in (* split into lists with the same head as the first, and lists with different heads *)
              (List.fold_left (fun x y -> x @ (List.tl y)) h r)::(combineHeads l) (* combine all the lists with the same head, then recurse on the remaining lists *)
            
            combineHeads [[1;2;3];[1;4;5;];[2;3;4];[1];[1;5;7];[2;5];[3;4;6]];;
            - : int list list = [[1; 2; 3; 4; 5; 5; 7]; [2; 3; 4; 5]; [3; 4; 6]]
            

            但是这不会很快(partition、fold_left 和 concat 都是 O(n))。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2019-12-15
              • 1970-01-01
              • 2016-09-04
              • 1970-01-01
              • 2019-11-19
              • 2020-12-23
              • 1970-01-01
              相关资源
              最近更新 更多