我有一个看起来有点像这样的C#方法:
bool Eval() {
// do some work
if (conditionA) {
// do some work
if (conditionB) {
// do some work
if (conditionC) {
// do some work
return true;
}
}
}
return false;
}
在F#中,由于强制性的其他分支,这最终看起来更加丑陋:
let eval() =
// do some work
if conditionA then
// do some work
if conditionB then
// do some work
if conditionC then
// do some work
true
else
false
else
false
else
false
在F#中写一个更简洁的方法是什么?
答案 0 :(得分:13)
module Condition =
type ConditionBuilder() =
member x.Bind(v, f) = if v then f() else false
member x.Return(v) = v
let condition = ConditionBuilder()
open Condition
let eval() =
condition {
// do some work
do! conditionA
// do some work
do! conditionB
// do some work
do! conditionC
return true
}
答案 1 :(得分:9)
如评论中所述,您可以反转条件。这简化了C#代码,因为您可以编写:
if (!conditionA) return false;
// do some work
虽然F#没有命令性返回(如果你想返回,你需要真假分支),它实际上也简化了这段代码,因为你可以写:
let eval() =
// do some work
if not conditionA then false else
// do some work
if not conditionB then false else
// do some work
if not conditionC then false else
// do some work
true
您仍然需要多次编写false
,但至少您不必将代码缩进太多。有无限数量的复杂解决方案,但这可能是最简单的选择。至于更复杂的解决方案,您可以使用F# computation expression that allows using imperative-style returns。这类似于丹尼尔的计算,但更强大。
答案 2 :(得分:6)
好吧,既然'做一些工作'已经势在必行(大概),那么我认为
let eval() =
let mutable result = false
... // ifs
result <- true
... // no more elses
result
更短更合理。 (换句话说,else
仅对返回值的if
表达式是必需的;因为您正在执行命令式工作,请使用不需要if
的{{1}}语句。)
答案 3 :(得分:6)
请不要害怕提取功能。这是控制复杂逻辑的关键。
let rec partA () =
// do some work
let aValue = makeA ()
if conditionA
then partB aValue
else false
and partB aValue =
// do some work
let bValue = makeB aValue
if conditionB
then partC bValue
else false
and partC bValue =
// do some work
conditionC
答案 4 :(得分:4)
使用Option
模块中的高阶函数可以使这个流非常干净,没有任何可变状态:
let Eval () =
// do some work
if not conditionA then None else
// do some work
Some state
|> Option.bind (fun state ->
if not conditionB then None else
// do some work
Some state')
|> Option.bind (fun state ->
if not conditionC then None else
// do some work
Some true)
|> defaultArg <| false
或者为了进一步说明,使用命名函数而不是lambdas:
let Eval () =
let a () =
if not conditionA then None else
// do some work
Some state
let b state =
if not conditionB then None else
// do some work
Some state'
let c state =
if not conditionC then None else
// do some work
Some true
// do some work
a () |> Option.bind b |> Option.bind c |> defaultArg <| false
答案 5 :(得分:1)
你可以将你的代码变成一种真值表,这取决于你的真实案例可能会使它更明确:
let condA() = true
let condB() = false
let condC() = true
let doThingA() = Console.WriteLine("Did work A")
let doThingB() = Console.WriteLine("Did work B")
let doThingC() = Console.WriteLine("Did work C")
let Eval() : bool =
match condA(), condB(), condC() with
| true, false, _ -> doThingA(); false;
| true, true, false -> doThingA(); doThingB(); false;
| true, true, true -> doThingA(); doThingB(); doThingC(); true;
| false, _, _ -> false;