Как рассчитать результат на основе многих других дорогих вычислений в 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
что это будет при неудаче.