7

私は次のように見える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#で書くためのよりクリーンな方法は何でしょうか?

4

6 に答える 6

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
  }
于 2012-08-15T21:15:49.357 に答える
9

コメントで述べたように、条件を逆にすることができます。これにより、次のように記述できるため、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必要がありますが、少なくともコードをインデントしすぎる必要はありません。複雑なソリューションは無数にありますが、これがおそらく最も単純なオプションです。より複雑なソリューションについては、命令型の return を使用できる F# 計算式を使用できます。これは Daniel の計算に似ていますが、もう少し強力です。

于 2012-08-15T21:16:07.220 に答える
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 
于 2012-08-15T20:53:29.097 に答える
6

まあ、「何か仕事をする」ことはすでに必須なので(おそらく)、

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

短くて合理的です。(つまり、elseは値を返す式にのみ必須ifです。命令型の作業を行っているため、 をif必要としないステートメントを使用してくださいelse。)

于 2012-08-15T20:22:46.097 に答える
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

または、さらに明確にするために、ラムダではなく名前付き関数を使用します。

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
于 2012-08-15T20:48:40.133 に答える
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;
于 2012-08-17T07:00:40.540 に答える