在二维列表中合并具有相同标题的列表(OCaml)
我正在使用OCaml中的列表列表,我正在尝试编写一个函数,将共享同一头部的所有列表组合在一起。这就是我目前所拥有的,我使用了List.hd内置函数,但毫不奇怪,我得到了失败“hd”错误: 例如,如果我有以下列表:在二维列表中合并具有相同标题的列表(OCaml),ocaml,Ocaml,我正在使用OCaml中的列表列表,我正在尝试编写一个函数,将共享同一头部的所有列表组合在一起。这就是我目前所拥有的,我使用了List.hd内置函数,但毫不奇怪,我得到了失败“hd”错误: 例如,如果我有以下列表: [[Sentence; Quiet]; [Sentence; Grunt]; [Sentence; Shout]] 我想把它组合成: [[Sentence; Quiet; Grunt; Shout]] 我编写的函数uniq只是删除列表中的所有重复项。请让我知道我将如何完成这项工作。
[[Sentence; Quiet]; [Sentence; Grunt]; [Sentence; Shout]]
我想把它组合成:
[[Sentence; Quiet; Grunt; Shout]]
我编写的函数uniq只是删除列表中的所有重复项。请让我知道我将如何完成这项工作。提前谢谢 首先,我通常避免使用像
List.hd
这样的函数,因为模式加工通常更清晰,更不容易出错。在这种情况下,您的if
可以替换为受保护的模式(模式后的when
子句)。我认为造成错误的原因是当t
为[]
时,代码失败;保护模式通过使案例更显式来帮助避免这种情况。因此,当x=y作为match
表达式中的子句时,可以执行(x::xs)::(y::ys)::t检查列表前两个元素的头是否相同。在OCaml中,除了防护装置之外,具有几个相同的连续模式并不少见
进一步的事情:你不需要[]@nlist
——这和写nlist
是一样的
而且,它看起来像您的nlist@h
和类似的表达式试图在将列表传递给递归调用之前连接列表;然而,在OCaml中,函数应用程序的绑定比任何操作符都要紧密,因此它实际上将递归调用的结果附加到h
我没有这个函数的正确版本。但是我会先用保护模式来编写它,然后看看这能让你在多大程度上完成它。考虑使用映射或哈希表来跟踪头部以及为每个头部找到的元素。如果具有相同标题的列表不相邻,则nlist
辅助列表没有多大帮助,如本例所示:
# combineSameHead [["A"; "a0"; "a1"]; ["B"; "b0"]; ["A"; "a2"]]
- : list (list string) = [["A"; "a0"; "a1"; "a2"]; ["B"; "b0"]]
您想要的操作有一个简单的递归描述:递归地处理列表的尾部,然后使用head执行“插入”操作,该操作查找以相同head开头的列表,如果找到,则插入除head之外的所有元素,并在末尾附加它。然后,您可以反转结果以获得预期的列表列表
在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)
我可能会按照安东纳科斯的建议做点什么。这将完全避免在列表中搜索的O(n)开销。您还可能发现,使用StringSet.tstringmap.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 []
仅使用标准库,您就可以做很多事情:
(* 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]]
但这不会很快(分区、左折叠和concat都是O(n))
(* 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]]