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

Best way to do tryMax and tryMin in F#?

提问人:sdgfsdh 提问时间:5/29/2020 更新时间:4/5/2021 访问量:255

问:

假设我有一个,如果有任何元素或其他元素,我想返回最大的。F# 似乎没有内置。seqNone

这是我的尝试:

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
  • 这种方法有什么问题吗?
  • 有没有内置的解决方案?
F# 序列

评论


答:

7赞 Abel 5/29/2020 #1

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

如果要防止重复计算,可以使用 ,或者根本不使用(使用 或代替)。或者使用 fold,它只迭代一次:Seq.cacheSeqListArray

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

这应该;)要小得多。

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

评论

0赞 Bent Tranberg 4/5/2021
这些不是内置的,这很烦人。我想知道是否有添加它们的建议。所有四个。
0赞 Abel 4/7/2021
@BentTranberg,我同意。这里只提到:github.com/fsharp/fslang-suggestions/issues/...,但不是作为添加它的建议。请随时在此处的 F# 建议存储库中创建请求:github.com/fsharp/fslang-suggestions
2赞 Brian Berns 9/3/2020 #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

全套:

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

评论

0赞 Abel 4/7/2021
我很欣赏在下面扩展我的原始答案,您可以在其中找到这种方法的解释和用法以及替代方案。虽然我建议使用内置函数,因为它们可能更好地内联/优化并增加可读性。minmax