从文件/数据库创建区分联合数据

时间:2013-01-28 19:24:34

标签: f# discriminated-union

我有像这样的表达式的歧视联盟(EQ =; GT&gt ;;等)

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

我想从保存在文件/数据库中的表达式创建DU实例。 我该怎么做?如果不可行,在F#中处理它的最佳方法是什么?

Daniel:这些表达式以前缀格式(如上所述)保存为文本,并将在F#中进行解析。感谢。

2 个答案:

答案 0 :(得分:3)

如果您只是想知道如何使用DU建模这些表达式,可以采用以下方法:

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#Programming wikibook中也有一些good examples

如果您需要解析这些表达式,我强烈推荐FParsec

答案 1 :(得分: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)))"