在 F# 中,我找不到根据内部集合中项目的属性查询集合的直观方法。例如,考虑地址簿的场景。它包含多个人的记录,每个人可以有多个电子邮件地址或电话号码。类型有:
type Email = {Email : string}
type PhoneNumber = {Number : string}
type Person = {Name : string; EmailAdresses : seq<Email>; PhoneNumbers : seq<PhoneNumber>}
我想编写一个函数来尝试根据电话号码查找人员。由于电话号码是唯一的,我希望该函数返回一个人或不返回任何人,例如函数签名变为
string->seq<Person>->Person option
我想出了
let ``find person by phone number`` number ppl=
ppl |> Seq.tryFind(fun r -> Seq.exists (fun p -> p.Number = number) r.PhoneNumbers)
Q1:我认为这个查询可读性不太好,可以用任何清洁剂编写吗?
Q2:我还希望能够查询其他内部集合,例如电子邮件地址。我不想定义另一个函数通过电子邮件地址查找人员
,而是想探索更通用的方法。作品如下:
let findPerson (propSelector : Person -> seq<'a>) (pred : seq<'a> -> bool) ppl =
ppl |> Seq.tryFind (propSelector >> pred)
(为了清楚起见,包括类型。)要调用这个函数,我必须这样做
ppl |> findPerson (fun x -> x.EmailAdresses) (fun p -> p |> Seq.exists (fun n -> n.Email = "john.doe@nowhere.com"))
lambda 再次变得丑陋,尤其是第二个。关于如何改进此代码有什么建议吗?
最佳答案
type Email = string
type PhoneNumber = string
type Name = string
type Person =
{Name:Name; EmailAddresses:seq<Email>; PhoneNumbers:seq<PhoneNumber>}
let findPerson (propSelector : Person -> seq<'a>) (pred : 'a -> bool) =
Seq.tryFind (propSelector >> Seq.exists pred)
let ``find person with john doe email``:(seq<Person> -> Person option) =
findPerson
(fun person -> person.EmailAddresses)
(fun email -> email = "john.doe@nowhere.com")
// .. start with a sequence of people
ppl |> ``find person with john doe email``
(* Alternate *)
ppl |> findPerson
(fun person -> person.EmailAddresses)
(fun email -> email = "john.doe@nowhere.com")
注意:类型可以很简单:
type Email = string
在 findPerson 中,您不需要提及 ppl,因为没有它您将返回一个采用 seq 的函数。比较
let findPerson (propSelector : Person -> seq<'a>) (pred : 'a -> bool) =
Seq.tryFind (propSelector >> Seq.exists pred)
至
let findPerson (propSelector : Person -> seq<'a>) (pred : 'a -> bool) ppl =
ppl |>Seq.tryFind (propSelector >> Seq.exists pred)
我已将 Seq.exists
调用移至 findPerson 方法中,以便在传入的 lambda 中不需要它。这允许传入的函数专注于该项目,而不是该项目整个序列。据推测,该方法需要一个序列属性。
最终的调用可以是调用
let ``find person with john doe email``:(seq<Person> -> Person option) =
findPerson
(fun person -> person.EmailAddresses)
(fun email -> email = "john.doe@nowhere.com")
或者简单地
ppl |> findPerson
(fun person -> person.EmailAddresses)
(fun email -> email = "john.doe@nowhere.com")
[编辑]
此外,您可以考虑将函数通过管道连接在一起,而不是使用括号。我认为这样读起来更自然一些。
let findPerson (propSelector : Person -> seq<'a>) (pred : 'a -> bool) =
propSelector >> Seq.exists pred |> Seq.tryFind
产生这个
type Email = string
type PhoneNumber = string
type Name = string
type Person =
{Name:Name; EmailAddresses:seq<Email>; PhoneNumbers:seq<PhoneNumber>}
let findPerson (propSelector : Person -> seq<'a>) (pred : 'a -> bool) =
propSelector >> Seq.exists pred |> Seq.tryFind
ppl |> findPerson
(fun person -> person.EmailAddresses)
(fun email -> email = "john.doe@nowhere.com")
关于lambda - F# 查询内部集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32028353/