簡體   English   中英

如何重構所需的else子句?

[英]How can I refactor out the required else clause?

我有一個看起來像這樣的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#中,由於強制執行else分支,最終看起來很難看:

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#編寫此代碼的更干凈的方法是什么?

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#沒有強制性的返回值(如果要返回,則需要true和false分支),但實際上它也簡化了這段代碼,因為您可以編寫:

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 ,但至少不必使代碼縮進太多。 無限數量的復雜解決方案,但這可能是最簡單的選擇。 對於更復雜的解決方案,您可以使用允許使用命令式returnF#計算表達式 這類似於Daniel的計算,但功能更強大。

好吧,既然“做一些工作”已經是必須的(大概),那么我認為

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

較短且合理。 (換句話說, else僅對於返回值的if表達式是必需的;由於您正在執行命令性工作,請使用不需要else if語句。)

請不要害怕提取函數。 這是控制復雜邏輯的關鍵。

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

或為了進一步清楚起見,使用命名函數而不是lambda:

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;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM