Как рассчитать результат на основе многих других дорогих вычислений в F#

Предполагая, что у меня есть следующий псевдо-C# код:

TResult MyMethod()
{
    var firstTry = SomeExpensiveComputation1();
    if (firstTry.IsSuccessful) return firstTry;

    var secondTry = SomeExpensiveComputation2();
    if (secondTry.IsPartiallySuccessful)
    {
        var subTry1 = SomeExpensiveComputationOn2_1(secondTry);
        if (subTry1.IsSuccessful) return subTry1;

        var subTry1 = SomeExpensiveComputationOn2_2(secondTry);
        if (subTry1.IsSuccessful) return subTry1;
    }

    return LastExpensiveComputationThatNeverFails();
}

Если бы я сделал это в F#, это выглядело бы так:

let MyMethod () =
    let firstTry = SomeExpensiveComputation1 ()
    if firstTry.IsSuccessful then firstTry else
        let secondTry = SomeExpensiveComputation2 ()
        if secondTry.IsSuccessful then
            let subTry1 = SomeExpensiveComputationOn2_1 ()
            if subTry1.IsSuccessful then subTry1 else
                let subTry2 = SomeExpensiveComputationOn2_2 ()
                if subTry2.IsSuccessful then subTry2 else LastExpensiveComputationThatNeverFails ()
        else
            LastExpensiveComputationThatNeverFails()

Как вы можете видеть выше, я должен был повторить LastExpensiveComputationThatNeverFails дважды. Это не обязательно вызов метода, это может быть много строк встроенных вычислений (например, попытаться получить какое-то значение из кэша, если его не существует, вычислить его.) Можно выполнить рефакторинг кода в другую функцию, но я все еще не нравится, как один и тот же код, даже если это всего одна строка, должен быть написан дважды (или больше), так как это приводит к дублированию и грязному обслуживанию. Как правильно написать такой код на F#?

1 ответ

Решение

Я думаю, что это хорошо, чтобы сделать LastExpensiveComputationThatNeverFails локальная функция, которая вызывается всякий раз, когда нужен результат.

Тем не менее, можно также изменить операции для возврата Option<_> и использовать встроенные функции комбинатора.

let MyMethod () =
  SomeExpensiveComputation1 ()
  |> Option.orElseWith
    ( fun () -> 
        SomeExpensiveComputation2 ()
        |> Option.bind (fun _ -> SomeExpensiveComputationOn2_1 () |> Option.orElseWith SomeExpensiveComputationOn2_2)
    )
  |> Option.orElseWith LastExpensiveComputationThatNeverFails

Option.orElseWith LastExpensiveComputationThatNeverFails выполняется только если предыдущий результат None что это будет при неудаче.

Другие вопросы по тегам