lambda - F# 查询内部集合

标签 lambda f#

在 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/

相关文章:

java - 我可以在运行时查询 lambda 函数的生命周期吗?

python - lambda 函数如何在 python 中引用它的参数?

python - 为什么涉及 list.index() 调用的 lambda 如此慢?

.net - 了解 F# 静态成员约束(构造小于通用警告)

json - F# JSON Type Provider,不序列化空值

f# - 避免嵌套模式匹配(可能与monad)

python - 如何在 map(func, iter) 中动态定义 'func' 和 'iter'?

java - 有什么办法可以规避 lambda 表达式的类型化?

count - 序列计数元素数

f# - 空隙与单位相反