OCaml:计算对列表中的不同值

时间:2016-01-23 07:48:59

标签: ocaml

我有一对配对列表

let myList=[(0,1);(0,2);(0,3);(1,5);(2,4);(3,5);(5,4);(5,6);(4,3)];;

为了计算列表中存在的每个单独的不同值,我有这个程序

let rec flat lst  visited =
match lst with
[]->visited
| (x,y)::xs -> flat xs (x::y::visited)) ;;


let newLst = flat myList [];;

val newLst : int list =
  [4; 3; 5; 6; 5; 4; 3; 5; 2; 4; 1; 5; 0; 3; 0; 2; 0; 1]

let rec count lista = 
match lista with  
[]->0
| x::xs -> 
if (List.mem x xs) then count xs
else 1+count xs;;

count newLst;;
- : int = 7

代码运行正常但我的问题是:

有更优雅或有效的方法吗? 例如一个独特的功能,而不是两个

4 个答案:

答案 0 :(得分:3)

您的方法有效,简单易懂。唯一的缺点是,您的代码使用Shlemiel the painter's algorithm。这意味着,处理时间表现为列表大小的二次函数。

如果要删除它,最好使用sets:将列表中的所有数字添加到集合中并计算其大小。现在,时间性能在 n log(n)中,并且可以更好地扩展。

let myList=[(0,1);(0,2);(0,3);(1,5);(2,4);(3,5);(5,4);(5,6);(4,3)]

module IntegerSet = Set.Make(struct
    type t = int
    let compare = Pervasives.compare
  end)

let count lst0 =
  let rec loop acc lst =
    match lst with
    | [] -> IntegerSet.cardinal acc
    | (a,b)::tl -> loop IntegerSet.(add b (add a acc)) tl
  in
  loop IntegerSet.empty lst0

此代码使用累加器 acc ,它通过迭代填充 在列表上。读取完所有列表后,将返回累加器中的元素数。

答案 1 :(得分:2)

我不会争论优雅...... 编写代码的另一种方法:使用折叠操作。 你的扁平函数可以这样写:

let flat  = List.fold_left (fun acc (x,y) -> x::y::acc) [] ;;

答案 2 :(得分:2)

您的解决方案基本上是如何在不广泛诉诸库函数的情况下完成的(并且以二次最坏情况性能为代价)。您可以使用List库中的函数来获得更简单的解决方案,但虽然这样做更简单,但它主要教您如何使用该库,而不是将OCaml作为一种语言[1]。也就是说,这是一个解决方案:

let myList=[(0,1);(0,2);(0,3);(1,5);(2,4);(3,5);(5,4);(5,6);(4,3)]

let count l =
  let open List in
  let (a, b) = split l in length (sort_uniq compare (a @ b))

let () =
  Printf.printf "=> %d\n" (count myList)

这使用List.split和列表追加运算符@将一组int的列表转换为整数列表,然后对其进行排序并删除重复项(List.sort_uniq),然后使用List.length计算结果。由于sort_uniq,这在时间O(n * log(n))中运行。

替代解决方案是使用SetHashtbl模块以比List.mem更有效的方式跟踪重复项,从而避免二次最坏情况时间(但也可以代码在这个过程中更复杂。)

[1]我在这里假设您正在学习OCaml,因此工业强度解决方案不一定是帮助您学习过程的最佳解决方案,具体取决于您所处的位置。

答案 3 :(得分:1)

优雅没有特定的含义,所以很难回答。

我认为这是解决问题的一种相当不错的方法。如果你想象你有很多不同的结构(对,树等列表),那么翻译成平面列表然后以不同方式处理列表的想法会有很好的感觉。

你的解决方案的一个问题是它在最坏的情况下是二次的,因为你正在搜索n对的长度为0,1,2,... n * 2的列表。

我怀疑这不应该是生产代码,因此计算复杂性可能并不重要。

如果您要在生产代码中执行此操作,其中列表很长且效率很重要,您可以直接在对列表上进行计数。而且你不会继续在列表中搜索重复项。您可以使用某种设置(甚至可能是类似矢量的设置)来跟踪您所看到的内容。很可能这对你的预期用途来说太过分了(看起来像是我的课程作业)。