在 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(使用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

用法:

> 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