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

If you just want to know how to model these expressions using DUs, here's one way:

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))))

Now, this may be too "loose," i.e., it permits expressions like And(Value(1), Value(2)), which may not be valid according to your grammar. But this should give you an idea of how to approach it.

There are also some good examples in the F# Programming wikibook.

If you need to parse these expressions, I highly recommend 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 回答