使用F#基于另一个列表减少列表的最快方法

时间:2015-09-02 21:59:40

标签: f#

根据另一个列表中的项目,在F#中删除一个列表中的项目的最有效方法是什么?

例如:
seq1 = ["blue"; "green"; "red"; "green" ...]
          seq2 = ["soda"; "green"; "pop" ...]

seq1最初有50,000个项目 seq2有12并随着时间的推移继续增长

如果该值在seq2

中,我想要删除seq1的所有实例

我有以下代码,这个代码和我能做的一样慢 - 不是我想要的。

let result = seq1 |> Seq.filter(fun a -> (Seq.exists(fun name -> name = a) seq2) = false)

我试图在功能上找到禁食方式(没有循环等)

谢谢: - )

1 个答案:

答案 0 :(得分:4)

如果seq1相对较长并且seq2相对较短,那么您可以从seq2的元素创建一个集合,然后使用该集合的Contains方法检查它是否包含指定的元素。查找集合比使用Seq.exists的序列查找要快得多。

我使用基于您的数字的简单脚本对此进行测试:

#time
let seq1 = Array.init 50000 (fun i -> ["blue"; "green"; "red"].[i%3])
let seq2 = Array.init 12 (fun i -> [ "soda"; "green"; "pop"].[i%3])

现在,这里有几个选项(我将它们包装在for i in 1 .. 10 do中以获得更合理的数字,然后将其除以10):

// 15ms - this is the original version, but I added `Array.ofSeq` to materialize it
let result = seq1 |> Seq.filter(fun a -> 
   (Seq.exists(fun name -> name = a) seq2) = false) |> Array.ofSeq

// 12ms - this is using `Array.filter` directly, which turns out to be as slow
let result = seq1 |> Array.filter(fun a -> 
  (Seq.exists(fun name -> name = a) seq2) = false) 

// 2ms - using `set.Contains` is much faster, even when we create the set each time
let l = set seq2
let result = seq1 |> Array.filter(fun a -> l.Contains a = false)

请注意,我没有将set seq2调用推出循环 - 如果你这样做,它甚至更快(你只需要在更改seq2时创建集合然后你可以保持它)。