可选择采用序列中的第一项

Dan*_*iel 16 f# idioms sequence

我需要一个类似的函数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
Run Code Online (Sandbox Code Playgroud)

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

  1. 是否有一种特别惯用的方法呢?
  2. 由于没有内置Seq.tryHead函数,这是否表明这不是必需的,是不常见的,或者在没有函数的情况下更好地实现?

UPDATE

tryHead已添加到F#4.0的标准库中.

Ste*_*sen 14

我认为(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)
Run Code Online (Sandbox Code Playgroud)

而不是像

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