在 F# 中执行 tryMax 和 tryMin 的最佳方法?
Best way to do tryMax and tryMin in F#?
假设我有一个 seq
,如果有任何元素,我想 return 最大,否则 None
。 F# 似乎没有这个内置的。
这是我的尝试:
let tryMax xs =
if Seq.isEmpty xs
then
None
else
Seq.max xs |> Some
let tryMin xs =
if Seq.isEmpty xs
then
None
else
Seq.min xs |> Some
- 这种方法有什么问题吗?
- 是否有内置的解决方案?
我认为你的方法总体上是好的。有一个答案现在已删除,建议使用 try/with
通过捕获空序列的错误来防止对第一项进行双重评估,但这也可能很昂贵。
如果你想防止双重计算,你可以使用Seq.cache
,或者根本不使用Seq
(使用List
或Array
代替)。或者使用 fold,它只迭代一次:
module Seq =
let tryMin sq =
sq
|> Seq.fold(fun x y ->
match x with None -> Some y | Some x -> Some(min x y)) None
用法:
> Seq.tryMin Seq.empty<int>;;
val it : int option = None
> Seq.tryMin (Seq.singleton 2L);;
val it : int64 option = Some 2L
> Seq.tryMin (seq { 2; 3});;
val it : int option = Some 2
> Seq.tryMin (seq { 2; -3});;
val it : int option = Some -3
一种可能更快的方法(我没有计时)是防止在每个最小或最大计算结果上创建 option
,同时防止第一个的多次迭代项目。
这应该 GC 压力也小得多;)。
module Seq =
let tryMin (sq: seq<_>) =
use e = sq.GetEnumerator()
// this returns false if there is no first item
if e.MoveNext() then
let mutable result = e.Current
while e.MoveNext() do
result <- min e.Current result
Some result
else
None
用法:
> Seq.tryMin Seq.empty<int>;;
val it : int option = None
> Seq.tryMin (Seq.singleton 2L);;
val it : int64 option = Some 2L
> Seq.tryMin (seq { 2; 3});;
val it : int option = Some 2
> Seq.tryMin (seq { 2; -3});;
val it : int option = Some -3
FWIW,这里还有 tryMinBy
:
let tryMinBy projection (items : seq<_>) =
use e = items.GetEnumerator()
if e.MoveNext() then
let mutable minItem = e.Current
let mutable minValue = projection minItem
while e.MoveNext() do
let value = projection e.Current
if value < minValue then
minItem <- e.Current
minValue <- value
Some minItem
else
None
全套:
module Seq
let tryMinBy projection (items : seq<_>) =
use e = items.GetEnumerator ()
if e.MoveNext ()
then
let mutable minItem = e.Current
let mutable minValue = projection minItem
while e.MoveNext () do
let value = projection e.Current
if value < minValue
then
minItem <- e.Current
minValue <- value
Some minItem
else
None
let tryMaxBy projection (items : seq<_>) =
use e = items.GetEnumerator ()
if e.MoveNext ()
then
let mutable maxItem = e.Current
let mutable maxValue = projection maxItem
while e.MoveNext () do
let value = projection e.Current
if value > maxValue
then
maxItem <- e.Current
maxValue <- value
Some maxItem
else
None
let tryMin (items : seq<_>) =
use e = items.GetEnumerator ()
if e.MoveNext ()
then
let mutable minItem = e.Current
while e.MoveNext () do
if e.Current < minItem
then
minItem <- e.Current
Some minItem
else
None
let tryMax (items : seq<_>) =
use e = items.GetEnumerator ()
if e.MoveNext ()
then
let mutable maxItem = e.Current
while e.MoveNext () do
if e.Current > maxItem
then
maxItem <- e.Current
Some maxItem
else
None
假设我有一个 seq
,如果有任何元素,我想 return 最大,否则 None
。 F# 似乎没有这个内置的。
这是我的尝试:
let tryMax xs =
if Seq.isEmpty xs
then
None
else
Seq.max xs |> Some
let tryMin xs =
if Seq.isEmpty xs
then
None
else
Seq.min xs |> Some
- 这种方法有什么问题吗?
- 是否有内置的解决方案?
我认为你的方法总体上是好的。有一个答案现在已删除,建议使用 try/with
通过捕获空序列的错误来防止对第一项进行双重评估,但这也可能很昂贵。
如果你想防止双重计算,你可以使用Seq.cache
,或者根本不使用Seq
(使用List
或Array
代替)。或者使用 fold,它只迭代一次:
module Seq =
let tryMin sq =
sq
|> Seq.fold(fun x y ->
match x with None -> Some y | Some x -> Some(min x y)) None
用法:
> Seq.tryMin Seq.empty<int>;;
val it : int option = None
> Seq.tryMin (Seq.singleton 2L);;
val it : int64 option = Some 2L
> Seq.tryMin (seq { 2; 3});;
val it : int option = Some 2
> Seq.tryMin (seq { 2; -3});;
val it : int option = Some -3
一种可能更快的方法(我没有计时)是防止在每个最小或最大计算结果上创建 option
,同时防止第一个的多次迭代项目。
这应该 GC 压力也小得多;)。
module Seq =
let tryMin (sq: seq<_>) =
use e = sq.GetEnumerator()
// this returns false if there is no first item
if e.MoveNext() then
let mutable result = e.Current
while e.MoveNext() do
result <- min e.Current result
Some result
else
None
用法:
> Seq.tryMin Seq.empty<int>;;
val it : int option = None
> Seq.tryMin (Seq.singleton 2L);;
val it : int64 option = Some 2L
> Seq.tryMin (seq { 2; 3});;
val it : int option = Some 2
> Seq.tryMin (seq { 2; -3});;
val it : int option = Some -3
FWIW,这里还有 tryMinBy
:
let tryMinBy projection (items : seq<_>) =
use e = items.GetEnumerator()
if e.MoveNext() then
let mutable minItem = e.Current
let mutable minValue = projection minItem
while e.MoveNext() do
let value = projection e.Current
if value < minValue then
minItem <- e.Current
minValue <- value
Some minItem
else
None
全套:
module Seq
let tryMinBy projection (items : seq<_>) =
use e = items.GetEnumerator ()
if e.MoveNext ()
then
let mutable minItem = e.Current
let mutable minValue = projection minItem
while e.MoveNext () do
let value = projection e.Current
if value < minValue
then
minItem <- e.Current
minValue <- value
Some minItem
else
None
let tryMaxBy projection (items : seq<_>) =
use e = items.GetEnumerator ()
if e.MoveNext ()
then
let mutable maxItem = e.Current
let mutable maxValue = projection maxItem
while e.MoveNext () do
let value = projection e.Current
if value > maxValue
then
maxItem <- e.Current
maxValue <- value
Some maxItem
else
None
let tryMin (items : seq<_>) =
use e = items.GetEnumerator ()
if e.MoveNext ()
then
let mutable minItem = e.Current
while e.MoveNext () do
if e.Current < minItem
then
minItem <- e.Current
Some minItem
else
None
let tryMax (items : seq<_>) =
use e = items.GetEnumerator ()
if e.MoveNext ()
then
let mutable maxItem = e.Current
while e.MoveNext () do
if e.Current > maxItem
then
maxItem <- e.Current
Some maxItem
else
None