在 f# 中使用自定义函数相交

Intersect using custom function in f#

我有两个列表。第一个是具有 Id 属性 的结构化记录列表,其中 Id 是一个字符串包装器。第二个是一个简单的字符串列表。

我需要在第一个列表中找到其 ID 在第二个集合中出现的所有记录。

在 c# 中,这将是微不足道的:

var intersect = list1.Where(x => list2.Contains(x.Id.ToString()))

也许有更有效的方法来获取交集,但是 c# 和 linq 使得获取所需的输出变得非常容易。

我是 f# 的新手,但在 f# 中这似乎是一个非常重要的操作。 f# 中的 Linq 不支持 lambda 比较器函数(据我所知),内置的过滤器函数似乎依赖于两个列表中实体之间的直接比较(指定自定义比较器并不容易)。

我试过以下方法:

list1 |> List.filter(fun x -> List.contains (x.Id.ToString() list2))

...但这会产生以下编译器错误:

This expression was expected to have type
    'bool'    
but here has type
    ''a list -> bool'

我真的希望有人会告诉我,我很笨,在 f# 中有一个非常简单的方法来做到这一点...拜托!

回答

原来是我的语法搞砸了。我 认为 需要下面的括号来强制 x.Id.ToString() 首先求值,但是参数周围的括号 List.contains 不是必需的

list1 |> List.filter(fun x -> List.contains (x.Id.ToString()) list2)

在 F# 中,函数参数由 空格 分隔,而不是逗号,每个参数周围的括号是可选的。您没有将整个参数列表括起来的括号 - 这将表示一个元组。

您写了:

list1 |> List.filter(fun x -> List.contains (x.Id.ToString(), list2))

但你可能是这个意思:

list1 |> List.filter (fun x -> List.contains (x.Id.ToString()) list2)

完整示例(放入 .fsx 文件):

type Foo =
  {
    Id : string
  }

let list1 =
  [
    { Id = "a" }
    { Id = "b" }
    { Id = "x" }
  ]

let list2 =
  [
    "a"
    "b"
    "c"
  ]

let list3 =
  list1 |> List.filter (fun x -> List.contains (x.Id.ToString()) list2)

printfn "%A" list3

// [{ Id = "a" }; { Id = "b" }]