我需要一个类似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
这样的具体类型也是一个原因。
现在,以上大部分都是推测,但我想我可能更接近客观。我认为很多时候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