我正在尝试制作一个只包含原始列表中每个元素的一个副本的列表。
例如[1; 2; 3; 3; 2]将是[1; 2; 3]或[“hi”;“the”;“world”;“hi”]将是[“hi”; “该”, “世界”]
我正在使用递归和模式匹配,而不是使用列表模块。
这是我的尝试和思考:我想浏览列表并查看头部,如果该元素存在于列表的尾部,那么我想获取该元素然后从现有元素中删除该元素列表
let rec common l =
match l with
| head :: tail -> if head = tail then head :: [] else head :: isolate(tail)
| [] -> []
答案 0 :(得分:3)
第一个答案非常简单,但它使用的AVL树具有O(log n)插入复杂性和大量内部指针分配以及每个项目的高内存消耗:
let common l = l |> Set.ofList |> Set.toList
时间结果如下:
#time "on"
let mutable temp = Unchecked.defaultof<_>
for i = 0 to 1000000 do
temp <- common [1;2;3;3;2;4;1;5;6;2;7;5;8;9;3;2;10]
()
Real: 00:00:03.328, CPU: 00:00:03.276, GC gen0: 826, gen1: 0, gen2: 0
AVL树已排序,因此不会保留原始顺序并返回已排序的元素,例如
common [1;2;3;3;2;4;1;5;6;2;7;5;10;8;9;3;2]
val it : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
SCG.HashSet
是一个命令式集合,带有O(1)插入/查找,每个项目的内存更少。保持重复值的私人记录是完美的数据结构。使用它,可以将公共函数编写为:
open System.Collections.Generic
let common (l:'T list) =
let set = HashSet()
let rec commonAux (input:'T list) (acc:'T list) : 'T list =
match input with
| head :: tail ->
if set.Add(head) then
commonAux tail (head :: acc)
else commonAux tail acc
| [] -> acc
commonAux l []
|> List.rev
甚至更简单:
let common (l:'T list) =
let set = HashSet()
List.fold (fun st t ->
if set.Add(t) then t :: st
else st
) [] l
|> List.rev
两者的时间安排相同:
Real: 00:00:01.105, CPU: 00:00:01.092, GC gen0: 722, gen1: 1, gen2: 0
Real: 00:00:01.168, CPU: 00:00:01.170, GC gen0: 730, gen1: 0, gen2: 0
因此,将List.fold
与HashSet
一起使用非常简单,快速且可以保留订单。这是一个很好的例子,当使用私有可变状态的能力是F#祝福并且比纯功能解决方案快得多,而外部功能保持“纯功能”而没有副作用。
为了完整起见,我们可以使用AVL集实现相同的折叠逻辑。它以与第一个答案相同的速度执行,是“纯函数”并保持原始顺序:
let common (l:'T list) =
let rec commonAux (input:'T list) (s) (acc:'T list) : 'T list =
match input with
| head :: tail ->
if Set.contains head s then commonAux tail s acc
else
commonAux tail (Set.add head s) (head :: acc)
| [] -> acc
commonAux l Set.empty []
|> List.rev
Real: 00:00:02.825, CPU: 00:00:02.808, GC gen0: 908, gen1: 1, gen2: 0
P.S。使用let common (l:'T list) = HashSet(l) |> List.ofSeq
并不能保证元素的顺序,并且比折叠解决方案慢c.2倍。
P.P.S。第二个asnwer的时间是:
Real: 00:00:07.504, CPU: 00:00:07.394, GC gen0: 1521, gen1: 1, gen2: 0
答案 1 :(得分:2)
我只会转换为一个集合并返回
let common l =
l |> Set.ofList |> Set.toList