Как рассчитать результат на основе многих других дорогих вычислений в F # - PullRequest
0 голосов
/ 26 февраля 2019

Предположим, у меня есть следующий псевдо-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 Ответ

0 голосов
/ 26 февраля 2019

Я думаю, что можно сделать 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, что будет при ошибке.

...