OCaml:在对列表中计算不同的值
我有一张成双的单子OCaml:在对列表中计算不同的值,ocaml,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 newLs
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
代码运行正确,但我的问题是:
有没有更优雅、更有效的方法
例如,一个独特的功能而不是两个优雅没有特定的含义,因此很难回答 我认为这是一个相当好的解决问题的方法。如果你想象你有很多不同的结构(成对列表、树等),那么将int转换成平面列表然后以不同的方式处理列表的想法会让你感觉很好 您的解决方案的一个问题是,在最坏的情况下,它是二次的,因为您正在搜索长度为0、1、2。。。n*2表示n对 我怀疑这不应该是生产代码,因此计算复杂性可能无关紧要
如果要在列表很长且效率很重要的生产代码中执行此操作,则可以直接在对列表上进行计数。而且你不会一直在列表中搜索重复项。你会使用某种集合(甚至有点像矢量集合)来跟踪你所看到的东西。对于您的预期用途来说,这很可能是过火了(对我来说,这看起来像是一个课堂作业)。我也不会为优雅而争论。。。 编写代码的另一种方式:使用折叠操作。 您的展开函数可以这样编写:
let flat = List.fold_left (fun acc (x,y) -> x::y::acc) [] ;;
您的解决方案基本上是如何做到这一点,而无需大量使用库函数(并以二次型最坏情况性能为代价)。您可以使用
列表
库中的函数来获得更简单的解决方案,但尽管这有点简单,但它将主要教您如何使用该库,而较少教您如何将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
和List append操作符@
将整数对列表转换为整数列表,然后对其排序并删除重复项(List.sort\u uniq
),然后使用List.length
对结果进行计数。由于sort\u uniq
,这将在时间O(n*log(n))内运行
替代解决方案是使用Set
或Hashtbl
模块以比List.mem
更有效的方式跟踪重复项,从而也避免了二次最坏情况时间(但也使过程中的代码更复杂)
[1] 我在这里假设您正在学习OCaml,因此,根据您所在的位置,工业级解决方案不一定是帮助您学习OCaml的最佳解决方案。您的方法可行,简单易懂。它唯一的缺点是,您的代码使用了。这意味着,处理时间表现为列表大小的二次函数 如果要删除它,可以使用:将列表中的所有数字添加到一个集合并计算其大小。现在,时间性能在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,该累加器acc通过迭代填充
在名单上。读取所有列表后,将返回累加器中的元素数。您可以编写一个直接处理对的函数,而不是列出所有值。它将更加高效,因为它不必创建中间列表。但是优雅和效率经常是冲突的:-)非常有趣的解决方案