我正在使用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只删除列表中的所有重复项。请告诉我如何完成此操作。提前谢谢!
答案 0 :(得分:3)
首先,我通常会避免像List.hd
这样的函数,因为模式加工通常更清晰,更不容易出错。在这种情况下,您的if
可以替换为保护模式(模式后面的when
子句)。我认为导致错误的原因是t
为[]
时代码失败;保护模式通过使案例更加明确来帮助避免这种情况。因此,您可以(x::xs)::(y::ys)::t when x = y
作为match
表达式中的子句来检查列表的前两个元素的头部是否相同。在OCaml中,除了守卫之外,有几个连续的模式是相同的并不罕见。
其他事项:您不需要[]@nlist
- 这与撰写nlist
一样。
此外,看起来你的nlist@h
和类似的表达式试图在将列表传递给递归调用之前连接列表;但是,在OCaml中,函数应用程序比任何运算符都绑定得更紧密,因此它实际上将递归调用的结果附加到h
。
我没有,副手,有一个正确版本的功能。但是我会先用保护模式编写它,然后看看你能用它做多远。
答案 1 :(得分: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)
答案 2 :(得分:0)
考虑使用Map
或哈希表来跟踪每个头部的头部和元素。如果具有相同磁头的列表不相邻,则nlist
辅助列表不是很有用,如下例所示:
# combineSameHead [["A"; "a0"; "a1"]; ["B"; "b0"]; ["A"; "a2"]]
- : list (list string) = [["A"; "a0"; "a1"; "a2"]; ["B"; "b0"]]
答案 3 :(得分:0)
我可能会按照antonakos的建议做一些事情。这将完全避免在列表中搜索的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 []
答案 4 :(得分:0)
您可以使用标准库做很多事情:
(* 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]]
然而,这不会很快(分区,fold_left和concat都是O(n))。