在 F# 中执行 tryMax 和 tryMin 的最佳方法?

sdg*_*sdh 5 f# seq

假设我有一个seq,如果有任何元素或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
Run Code Online (Sandbox Code Playgroud)
  • 这种方法有什么问题吗?
  • 是否有内置的解决方案?

Abe*_*bel 7

我认为你的方法总体上是好的。有一个现在已删除的答案,建议try/with通过捕获空序列的错误来防止对第一项进行双重评估,但这也可能很昂贵。

如果你想防止双重评估,你可以使用Seq.cache,或者根本不使用Seq(使用ListArray代替)。或者使用 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
Run Code Online (Sandbox Code Playgroud)

用法:

> 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
Run Code Online (Sandbox Code Playgroud)

一种可能更快的方法(我没有计时)是防止option在每个最小或最大计算结果上创建 ,同时防止第一项的多次迭代。

这也应该有更少的 GC 压力;)。

> 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
Run Code Online (Sandbox Code Playgroud)

用法:

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
Run Code Online (Sandbox Code Playgroud)


bri*_*rns 2

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
Run Code Online (Sandbox Code Playgroud)

全套:

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
Run Code Online (Sandbox Code Playgroud)