f#列表的交集

时间:2013-11-27 16:35:00

标签: list f# intersection

let rec mem list x = match list with
                 | [] -> false
                 | head :: tail -> 
                   if x = list.Head 
                   then true
                   else mem list.Tail x 

函数mem将列表和var X作为参数,并检查列表是否包含值X,如果匹配则返回true,如果是,则返回false。

let rec intersection list1 list2 = match list1 with
               | head :: tail -> match list2 with 
                     | head :: tail -> if mem list2 list1.Head = true 
                     then (*add the value to a list*) else intersection list1.Tail list2
               | [] -> failwith "Second list is empty"
       | [] -> failwith "First list is empty"

我对F#很新,我现在遇到的问题是我不知道如何在(将值添加到列表)中构建列表然后将值添加到它。我还没有测试过代码,因为我需要先完成这一步,以免出错,所以我不能100%确定它是如何工作的。

我正在尝试交叉2个列表,我知道它确实存在这个“Set.Intersect list1 list2”的函数。缩进在这里有点奇怪,因为我不想长行,但你可能会理解。

4 个答案:

答案 0 :(得分:11)

我喜欢使用集合运算符http://msdn.microsoft.com/en-us/library/ee353629.aspx 您可以使用Set.intersect (Set.ofList list1) (Set.ofList list2) |> Set.toList

答案 1 :(得分:7)

修复代码的最直接方法是编写类似下面代码的内容。

mem函数中,我只修复了缩进并将其更改为使用从模式匹配中获得的headtail,而不是通过list.Head和{{}获取它们{1}}(因为这样做更惯用,更安全):

list.Tail

let rec mem list x = match list with | [] -> false | head :: tail -> if x = head then true else mem tail x 中,当intersection是出现在两个列表中的元素时,诀窍是使用head::rest来构建结果列表(并且head是列表你可以通过递归方式将尾部应用于尾部)。您也不需要在rest上匹配,因为list2处理空列表很好:

mem

这不是超级有效的(假设 n let rec intersection list1 list2 = match list1 with | head :: tail -> let rest = intersection tail list2 if mem list2 head then head::rest else rest | [] -> [] 的长度而 m list1的长度,您可能需要到 m * n 步骤),但这可能不是重点。此外,list2不是尾递归的,因此它不适用于大型列表,但这是另一个 - 更高级的函数式编程主题。

最后,代码还会返回可能多次包含单个元素的列表 - 但我想这对你来说很好(例如intersection返回intersection [1;1;1] [1]但是如果你翻转参数你会得到只是[1;1;1]

答案 2 :(得分:1)

为了做到这一点,您需要跟踪正在构建的列表。执行此操作的最佳方法是定义一个辅助函数,该函数将列表构建为参数并将其包含在递归调用中

let intersection list1 list2 = 
  let rec inner list1 list2 builtList = 
    match list1 with
    | head :: tail -> 
      match list2 with 
      | head :: tail -> 
        if mem list2 list1.Head = true then 
          inner tail list2 (list1.Head :: builtList) 
        else
          inner tail list2 builtList
      | [] -> failwith "Second list is empty"
    | [] -> failwith "First list is empty"
  inner list1 list2 [] 

另一个评论是,在空列表中失败是不好的做法。只需将空列表视为没有元素的列表,因此不可能有交集

let intersection list1 list2 = 
  let rec inner list1 list2 builtList = 
    match list1 with
    | head :: tail -> 
      if mem list2 list1.Head = true then 
        inner tail list2 (list1.Head :: builtList) 
      else
        inner tail list2 builtList
    | [] -> builtList

  inner list1 list2 [] 

此版本有效,但最终会返回一个列表,其中的元素与它们在list1中出现的顺序相反。为了解决这个问题,我们可以使用回调来按正确的顺序构建列表

let intersection list1 list2 = 
  let rec inner list1 list2 builder = 
    match list1 with
    | head :: tail -> 
      if mem list2 list1.Head = true then 
        inner tail list2 (fun next -> list1.Head :: next)
      else
        inner tail list2 builder
    | [] -> (builder [])

  inner list1 list2 (fun x -> x)

答案 3 :(得分:0)

我晚了1000年参加晚会,但是如果有人在这个问题上遇到困难,那么第一条评论中的答案对我不起作用。但是,这总是我的错,我对F#还是很陌生。我提出了以下似乎很好的方法。 欢迎提出任何有关改进的意见,因为我的代码可能很愚蠢。

ps。在这一点上,我不想使用内置运算符,因为我正在为一个类这样做。

ps1。我不想使用if,我的教授更喜欢match(为什么?)

let rec intersect =
    function 
    | ([],[]) -> []
    | ([],_) -> []
    | (_,[]) -> []
    | (x::xtail,y::ytail) -> match x=y with
                             | true -> x::intersect(xtail,ytail)
                             | false -> intersect(xtail,y::ytail)