F# 中的 SingleOrDefault

psf*_*aki 2 linq f# c#-to-f#

类似于这个问题 -在 F# 中编写 LINQ 的SingleOrDefault的最惯用的方法是什么?

Fyo*_*kin 5

如果您希望函数null在序列为空时返回(或默认值类型),只需继续调用现有SingleOrDefault方法。您可以很好地从 F# 调用 C# 方法。但是请记住,大多数 F# 本机类型不接受空值,因此这可能并不总是可行的。

如果您希望您的函数返回一个选项类型,None当序列包含零个元素或多个元素时回退,您可以使用Seq.truncate截断到前两个元素:

let singleOrDefault s =
    match s |> Seq.truncate 2 |> Seq.toList with
    | [x] -> Some x
    | _ -> None
Run Code Online (Sandbox Code Playgroud)

或者类似地,对于也带有谓词的重载:

let singleOrDefaultP pred s =
    match s |> Seq.filter pred |> Seq.truncate 2 |> Seq.toList with
    | [x] -> Some x
    | _ -> None
Run Code Online (Sandbox Code Playgroud)

如果您更喜欢同时使用两个版本,那么将一个版本表达为另一个版本以保持干燥可能是有意义的:

let singleOrDefault s = singleOrDefaultP (fun _ -> true) s
Run Code Online (Sandbox Code Playgroud)

与使用额外Seq.isEmpty检查相比,此解决方案的优势在于序列仅评估一次,并且仅在需要时评估。

顺便说一句,这正是的默认实现SingleOrDefault所做的。

另请注意,None当序列包含多个元素时,此解决方案将返回,而不是像原来那样抛出异常。这是在 F# 中处理错误的首选、惯用方法。但是,如果您更喜欢原始方式,则可以通过向匹配项添加另一个案例来轻松实现:

let singleOrDefault s =
    match s |> Seq.truncate 2 |> Seq.toList with
    | [x] -> Some x
    | [_;_] -> failWith "Too many"  // replace with your favourite exception
    | _ -> None
Run Code Online (Sandbox Code Playgroud)

  • Errmmm, SingleOrDefault *throws* 如果有超过 1 个匹配项。`truncate` 不是一个好的选择 (3认同)