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

时间:2011-09-20 15:15:04

标签: f# sequence idioms

我需要一个类似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是我使用的那个。两个问题:

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

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

2 个答案:

答案 0 :(得分: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)

而非喜欢

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

答案 1 :(得分:1)

您可以定义:

let seqTryHead s = Seq.tryPick Some s

类型为seq<'a> -> 'a option。请注意,由于通用值限制,我不会进行beta-reduce。