如何重构所需的else子句?

我有一个看起来有点像这样的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#写这个更干净的方法是什么?

好吧,既然“做一些工作”已经势在必行(大概),那么我认为

 let eval() = let mutable result = false ... // ifs result <- true ... // no more elses result 

更短更合理。 (换句话说, else仅对于返回值的表达式是必需的;因为您正在执行命令式工作,所以请使用不需要else if语句。)

 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 } 

如评论中所述,您可以反转条件。 这简化了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#计算表达式 。 这类似于丹尼尔的计算,但更强大一些。

请不要害怕提取function。 这是控制复杂逻辑的关键。

 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 

使用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 

你可以将你的代码变成一种真值表,这取决于你的真实案例可能会使它更明确:

 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;