Лучший способ сделать tryMax и tryMin в F #? - PullRequest
4 голосов
/ 29 мая 2020

Предположим, у меня есть 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
  • Есть ли проблемы с этим подходом?
  • Есть ли для этого встроенное решение?

Ответы [ 2 ]

5 голосов
/ 29 мая 2020

Думаю, ваш подход в целом хорош. Был удален ответ, в котором предлагалось использовать 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 для каждого результата вычисления минимума или максимума, и в то же время предотвращение многократных итераций первого элемента.

Этот должен иметь гораздо меньшее давление G C;).

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
1 голос
/ 02 сентября 2020

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
...