How can I refactor out the required else clause?

后端 未结 6 440
春和景丽
春和景丽 2021-01-12 09:13

I have a C# method that looks a bit like this:

bool Eval() {
  // do some work
  if (conditionA) {
     // do some work
     if (conditionB) {
       // do s         


        
相关标签:
6条回答
  • 2021-01-12 09:47

    Well, since 'do some work' is already imperative (presumably), then I think that

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

    is shorter and reasonable. (In other words, else is only mandatory for if expressions that return values; since you're doing imperative work, use if statements that don't need an else.)

    0 讨论(0)
  • 2021-01-12 09:47

    Please don't be afraid to extract functions. This is key to controlling complex logic.

    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 
    
    0 讨论(0)
  • 2021-01-12 09:49

    As mentioned in the comments, you could inverse the condition. This simplifies the C# code, because you can write:

    if (!conditionA) return false;
    // do some work
    

    Although F# does not have imperative returns (if you want to return, you need both true and false branches), it actually simplifies this code a bit too, because you can write:

    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 
    

    You still have to write false multiple times, but at least you don't have to indent your code too far. There is an unlimited number of complex solutions, but this is probably the simplest option. As for more complex solution, you could use an F# computation expression that allows using imperative-style returns. This is similar to Daniel's computation, but a bit more powerful.

    0 讨论(0)
  • 2021-01-12 09:55

    Using the higher-order functions in the Option module can make this flow very cleanly without any mutable state:

    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
    

    Or for further clarity, using named functions rather than 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
    
    0 讨论(0)
  • 2021-01-12 09:55

    You could make your code into a kind of truth table, which depending on your real-world case might make it more explicit:

    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;
    
    0 讨论(0)
  • 2021-01-12 10:08
    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
      }
    
    0 讨论(0)
提交回复
热议问题