Seq.exactlyOne 的非抛出版本来测试单例序列
Non-throwing version of Seq.exactlyOne to test for a singleton sequence
更新:我为此创建了一个 UserVoice 请求:Expand on the Cardinality functions for Seq。
我需要 Seq.exactlyOne
的功能,但具有 Some/None 语义。换句话说,我需要 Seq.head
,或者,如果序列为空或包含多个项目,我什么都不需要。使用 Seq.exactlyOne
会在这种情况下抛出。
我不认为有一种内置的方式来获得它(尽管它听起来很微不足道,以至于人们会期望 Seq.singleton
有一个对应的方式)。我想到了这个,但感觉很费解:
let trySingleton sq =
match Seq.isEmpty sq with
| true -> None
| false ->
match sq |> Seq.indexed |> Seq.tryFind (fst >> ((=) 1)) with
| Some _ -> None
| None -> Seq.exactlyOne sq |> Some
给出:
> trySingleton [|1;2;3|];;
val it : int option = None
> trySingleton Seq.empty<int>;;
val it : int option = None
> trySingleton [|1|];;
val it : int option = Some 1
是否有更简单甚至内置的方法?我可以在 Seq.exactlyOne
上 try/catch,但这是围绕异常构建业务逻辑,我宁愿不这样做(而且它很昂贵)。
更新:
我不知道 Seq.tryItem
函数,这会使事情变得更简单:
let trySingleton sq =
match sq |> Seq.tryItem 1 with
| Some _ -> None
| None -> Seq.tryHead sq
(好多了,但还是感觉比较别扭)
为什么不通过命令式处理枚举数来解决问题?
let trySingleton' (xs : seq<_>) =
use en = xs.GetEnumerator()
if en.MoveNext() then
let res = en.Current
if en.MoveNext() then None
else Some res
else None
trySingleton' Seq.empty<int> // None
trySingleton' [1] // Some 1
trySingleton' [1;2] // None
我不知道这个有内置函数,但这里有另一种实现它的方法:
let tryExactlyOne xs =
match xs |> Seq.truncate 2 |> Seq.toList with
| [x] -> Some x
| _ -> None
FSI 演示:
> [42] |> List.toSeq |> tryExactlyOne;;
val it : int option = Some 42
> [42; 1337] |> List.toSeq |> tryExactlyOne;;
val it : int option = None
> Seq.empty<int> |> tryExactlyOne;;
val it : int option = None
更新:我为此创建了一个 UserVoice 请求:Expand on the Cardinality functions for Seq。
我需要 Seq.exactlyOne
的功能,但具有 Some/None 语义。换句话说,我需要 Seq.head
,或者,如果序列为空或包含多个项目,我什么都不需要。使用 Seq.exactlyOne
会在这种情况下抛出。
我不认为有一种内置的方式来获得它(尽管它听起来很微不足道,以至于人们会期望 Seq.singleton
有一个对应的方式)。我想到了这个,但感觉很费解:
let trySingleton sq =
match Seq.isEmpty sq with
| true -> None
| false ->
match sq |> Seq.indexed |> Seq.tryFind (fst >> ((=) 1)) with
| Some _ -> None
| None -> Seq.exactlyOne sq |> Some
给出:
> trySingleton [|1;2;3|];;
val it : int option = None
> trySingleton Seq.empty<int>;;
val it : int option = None
> trySingleton [|1|];;
val it : int option = Some 1
是否有更简单甚至内置的方法?我可以在 Seq.exactlyOne
上 try/catch,但这是围绕异常构建业务逻辑,我宁愿不这样做(而且它很昂贵)。
更新:
我不知道 Seq.tryItem
函数,这会使事情变得更简单:
let trySingleton sq =
match sq |> Seq.tryItem 1 with
| Some _ -> None
| None -> Seq.tryHead sq
(好多了,但还是感觉比较别扭)
为什么不通过命令式处理枚举数来解决问题?
let trySingleton' (xs : seq<_>) =
use en = xs.GetEnumerator()
if en.MoveNext() then
let res = en.Current
if en.MoveNext() then None
else Some res
else None
trySingleton' Seq.empty<int> // None
trySingleton' [1] // Some 1
trySingleton' [1;2] // None
我不知道这个有内置函数,但这里有另一种实现它的方法:
let tryExactlyOne xs =
match xs |> Seq.truncate 2 |> Seq.toList with
| [x] -> Some x
| _ -> None
FSI 演示:
> [42] |> List.toSeq |> tryExactlyOne;;
val it : int option = Some 42
> [42; 1337] |> List.toSeq |> tryExactlyOne;;
val it : int option = None
> Seq.empty<int> |> tryExactlyOne;;
val it : int option = None