我需要一个类似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。
答案 0 :(得分:14)
我认为(b)可能是最惯用的,出于同样的原因@Ramon给出了。
我认为Seq.tryHead
的缺失只意味着它并不常见。
我不确定,但我的猜测是,一般来说,使用Hindley-Milner类型推断的函数式语言在集合类型上实现此类特定函数时很少,因为重载不可用,并且可以简单地组成高阶函数。
例如,C#Linq扩展比F#的Seq
模块中的函数更详尽(它本身比具体集合类型上的函数更详尽),甚至还有IEnumerable.FirstOrDefault
。实际上,每个重载都有一个执行map
的变体。
我认为强调模式匹配和具体类型如list
也是一个原因。
现在,上述大部分内容都是猜测,但我认为我可能有一个更接近客观的概念。我认为很多时候tryPick
和tryFind
可以首先使用,而不是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。