2

このような式の識別共用体があります (EQ =; GT >; など)

  (AND (OR (EQ X 0)
           (GT X 10))
       (OR (EQ Y 0)
           (GT Y 10)))

ファイル/データベースに保存されたそのような式から DU のインスタンスを作成したいと考えています。どうすればいいのですか?実現不可能な場合、F# でアプローチする最善の方法は何ですか?

Daniel: これらの式は (上記のように) プレフィックス形式でテキストとして保存され、F# で解析されます。ありがとう。

4

2 に答える 2

3

DU を使用してこれらの式をモデル化する方法を知りたい場合は、次の 1 つの方法があります。

type BinaryOp =
  | EQ
  | GT

type Expr =
  | And of Expr * Expr
  | Or of Expr * Expr
  | Binary of BinaryOp * Expr * Expr
  | Var of string
  | Value of obj

let expr = 
  And(
    Or(
      Binary(EQ, Var("X"), Value(0)),
      Binary(GT, Var("X"), Value(10))),
    Or(
      Binary(EQ, Var("Y"), Value(0)),
      Binary(GT, Var("Y"), Value(10))))

さて、これは「ルーズ」すぎるかもしれません。つまり、And(Value(1), Value(2))あなたの文法によれば有効ではないかもしれない のような式を許可します。しかし、これにより、それにアプローチする方法のアイデアが得られるはずです。

F# プログラミング wikibook にも良い例がいくつかあります。

これらの式を解析する必要がある場合は、FParsecを強くお勧めします。

于 2013-01-28T19:35:35.917 に答える
2

ダニエルの答えは良いです。アクティブなパターンで構築された単純なトップダウン パーサーと共に、同様のアプローチを次に示します。

type BinOp = | And | Or
type Comparison = | Gt | Eq

type Expr =
| BinOp of BinOp * Expr * Expr
| Comp of Comparison * string * int

module private Parsing = 
    // recognize and strip a leading literal 
    let (|Lit|_|) lit (s:string) =
        if s.StartsWith(lit) then Some(s.Substring lit.Length)
        else None

    // strip leading whitespace
    let (|NoWs|) (s:string) =
        s.TrimStart(' ', '\t', '\r', '\n')

    // parse a binary operator
    let (|BinOp|_|) = function
    | Lit "AND" r -> Some(And, r)
    | Lit "OR" r -> Some(Or, r)
    | _ -> None

    // parse a comparison operator
    let (|Comparison|_|) = function
    | Lit "GT" r -> Some(Gt, r)
    | Lit "EQ" r -> Some(Eq, r)
    | _ -> None

    // parse a variable (alphabetical characters only)
    let (|Var|_|) s =
        let m = System.Text.RegularExpressions.Regex.Match(s, "^[a-zA-Z]+")
        if m.Success then
            Some(m.Value, s.Substring m.Value.Length)
        else
            None

    // parse an integer
    let (|Int|_|) s =
        let m = System.Text.RegularExpressions.Regex.Match(s, @"^-?\d+")
        if m.Success then
            Some(int m.Value, s.Substring m.Value.Length)
        else
            None

    // parse an expression
    let rec (|Expr|_|) = function
    | NoWs (Lit "(" (BinOp (b, Expr(e1, Expr(e2, Lit ")" rest))))) -> 
        Some(BinOp(b, e1, e2), rest)
    | NoWs (Lit "(" (Comparison (c, NoWs (Var (v, NoWs (Int (i, Lit ")" rest))))))) ->
        Some(Comp(c, v, i), rest)
    | _ -> None

let parse = function
| Parsing.Expr(e, "") -> e
| s -> failwith (sprintf "Not a valid expression: %s" s)

let e = parse @"
    (AND (OR (EQ X 0)
             (GT X 10))
         (OR (EQ Y 0)
             (GT Y 10)))"
于 2013-01-28T20:34:51.617 に答える