List f#列表的交集

List f#列表的交集,list,f#,intersection,List,F#,Intersection,函数mem将list和var X作为参数,检查list是否包含值X,如果包含值X,则返回true,如果不包含值X,则返回false let rec mem list x = match list with | [] -> false | head :: tail -> if x = list.Head then true

函数mem将list和var X作为参数,检查list是否包含值X,如果包含值X,则返回true,如果不包含值X,则返回false

let rec mem list x = match list with
                 | [] -> false
                 | head :: tail -> 
                   if x = list.Head 
                   then true
                   else mem list.Tail x 
我对F#很陌生,现在的问题是我不知道如何在(将值添加到列表中)中构造一个列表,然后将值添加到列表中。我还没有测试代码,因为我需要先完成这一步,以免出错,所以我不能100%确定它是如何工作的


我正在尝试相交2个列表,我知道它确实存在此“Set.intersect list1 list2”的函数。缩进在这里也有点奇怪,因为我不想说太多的行,但无论如何你都会理解的。

修复代码的最直接方法是编写如下代码

mem
函数中,我刚刚修复了缩进,并将其更改为使用模式匹配中获得的
head
tail
,而不是通过
list.head
list.tail
访问它们(因为这样做更惯用、更安全):

intersection
中,诀窍是当
head
是出现在两个列表中的元素时,使用
head::rest
构建一个结果列表(而
rest
是通过递归地将交集应用于尾部而得到的列表)。您也不需要在
list2
上进行匹配,因为
mem
可以很好地处理空列表:

let rec mem list x = 
  match list with
  | [] -> false
  | head :: tail -> 
    if x = head then true else mem tail x 
这不是非常有效(假设n是
list1
的长度,m是
list2
的长度,您可能需要多达m*n个步骤),但这可能不是重点。另外,
intersection
不是尾部递归的,因此它不能处理大型列表,但这是另一个更高级的函数式编程主题


最后,代码还将多次返回可能包含单个元素的列表-但我想这对您来说没什么问题(例如,
交叉点[1;1;1][1]
返回
[1;1;1]
,但如果翻转参数,您只会得到
[1]

为了做到这一点,您需要跟踪正在建立的列表。最好的方法是定义一个helper函数,该函数将生成的列表作为参数,并将其包含在递归调用中

let rec intersection list1 list2 = 
  match list1 with
  | head :: tail -> 
      let rest = intersection tail list2
      if mem list2 head then head::rest
      else rest
  | [] -> []
另一个评论是,在空列表上失败是一种不好的做法。只需将空列表视为没有元素的列表,因此不可能存在交集

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 [] 
此版本可以工作,但最终将返回一个列表,该列表中的元素与它们在
list1
中的显示顺序相反。为了解决这个问题,我们可以使用回调以正确的顺序建立列表

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 [] 

我喜欢使用集合运算符
你可以使用Set.intersect(Set.ofList list1)(Set.ofList list2)|>Set.toList

我迟到了1000年,但如果有人对这个问题感到不快,第一条评论中的答案对我来说不起作用。但是,这可能总是我的错,我对F#相当陌生。我提出了以下似乎很有效的方法。 任何关于改进的意见都是欢迎的,因为我的代码可能有点愚蠢

在这一点上,我不想使用内置操作符,因为我是为一个类这样做的

ps1。我不想使用
如果
,我的教授更喜欢
匹配
(idk为什么)

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