F#:过滤从另一个列表中的一个列表中找到的项目

时间:2009-10-01 20:29:38

标签: f#

说我有两个清单:

let a = [1 .. 1000]
let b = [250 .. 500]

如何获取包含值{1-249,501-1000}的新列表?

3 个答案:

答案 0 :(得分:9)

由于您的列表已排序,您可以使用此(非尾递归)函数在线性时间内解决此问题:

let rec except a b =
    match (a, b) with
    | [], x | x, [] -> x
    | x::xs, y::ys ->
        if x < y then x :: except xs (y::ys)
        elif x > y then y :: except (x::xs) ys
        else except xs ys

尾递归版:

let rec except_tail_recursive a b =
    let rec loop acc a b =
        match (a, b) with
        | [], x | x, [] -> (List.rev acc) @ x
        | x::xs, y::ys ->
            if x < y then loop (x::acc) xs (y::ys)
            elif x > y then loop (y::acc) (x::xs) ys
            else loop acc xs ys
    loop [] a b

答案 1 :(得分:8)

如果您需要纯F#解决方案,您的选择将根据您的要求而有所不同。如果您的列表不包含重复项目而且您不关心输出的顺序,则可以这样做:

(Set.of_list a) - (Set.of_list b) |> Set.to_list

如果您知道您的商品已经过分类,那么这应该有效并且效率很高:

let exclude =
  let rec exclude = function
    | [],_ -> [] 
    | a,[] -> a
    | (x::xs as l),(y::ys as r) -> 
        if x < y then x :: (exclude (xs, r))
        elif x > y then exclude (l, ys)
        else exclude (xs, ys)
  fun a b -> exclude (a,b)

如果您有两个可能包含重复项的列表,不一定要排序,您希望按a中出现的顺序得到结果,而您不关心性能,您可以这样做:

a |> List.filter (fun x -> not (List.contains x b))

答案 2 :(得分:5)

如果您正在使用3.5或更高版本的框架,则可以执行以下操作

let c = System.Linq.Enumerable.Except(a,b)

这不是一个纯粹的F#解决方案,但它完成了工作。返回将是IEnumerable<int>的实例,但不是F#列表。