我需要一个类似Seq.head的函数,但是在序列为空(即None)时返回seq<'T> -> 'T option而不是引发异常。

有无数种方法可以做到这一点。这里有几个:

let items = Seq.init 10 id
let a = Seq.tryFind (fun _ -> true) items
let b = Seq.tryPick Some items
let c = if Seq.isEmpty items then None else Some (Seq.head items)
let d =
  use e = items.GetEnumerator()
  if e.MoveNext() then Some e.Current
  else None


b是我使用的那个。两个问题:


有没有特别惯用的方式来做到这一点?
由于没有内置的Seq.tryHead函数,这是否表明这不是必需的,不常见的,或者没有函数也可以更好地实现?


更新
tryHead has been added to the standard library in F# 4.0

最佳答案

我认为(b)可能是最惯用的,原因与@Ramon给出的原因相同。

我认为缺少Seq.tryHead只是意味着它不是超级常见。

我不确定,但是我的猜测是,通常具有Hindley-Milner类型推断的功能语言在集合类型上实现此类特定功能的方法很少,因为过载不可用,并且可以很简单地完成高阶功能。

例如,C#Linq扩展比F#的Seq模块中的功能(其本身比具体集合类型上的功能更穷)更加详尽,甚至具有IEnumerable.FirstOrDefault。实际上,每个重载都有一个执行map的变量。

我认为强调模式匹配和list这样的具体类型也是一个原因。

现在,以上大部分都是推测,但我想我可能更接近客观。我认为很多时候tryPicktryFind可以代替filter |> tryHead首先使用。例如,我发现自己经常编写如下代码:

open System.Reflection
let ty = typeof<System.String> //suppose this type is actually unknown at compile time
seq {
    for name in ["a";"b";"c"] do
        yield ty.GetMethod(name)
} |> Seq.tryFind((<>)null)


而不是像

...
seq {
    for name in ["a";"b";"c"] do
        match ty.GetMethod(name) with
        | null -> ()
        | mi -> yield mi
} |> tryHead

09-05 18:19
查看更多