F#删除列表中的常用元素

时间:2016-01-29 22:36:32

标签: recursion f#

我正在尝试制作一个只包含原始列表中每个元素的一个副本的列表。

例如[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)
  | [] -> []

2 个答案:

答案 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.foldHashSet一起使用非常简单,快速且可以保留订单。这是一个很好的例子,当使用私有可变状态的能力是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