F#模式匹配:如何匹配共享相同参数的一组可能类型?

enz*_*nzi 3 f# pattern-matching fsyacc

我是F#的新手,并不太熟悉整个模式匹配的想法.我试图找到一个更好的解决方案,但我担心我甚至无法正确表达问题 - 我希望问题标题至少有些准确.

我想要做的是从中提取2个"参数" listMethod. listMethod是具有字符串和Expression"参数" 的几种类型之一(我怀疑参数是错误的术语):

    let (varDecl, listExpr) =
        match listMethod with 
        | Select (var, expr)  -> (var, expr)
        | Where (var, expr)   -> (var, expr)
        | Sum (var, expr)     -> (var, expr)
        | Concat (var, expr)  -> (var, expr)
Run Code Online (Sandbox Code Playgroud)

然后我继续使用varDecl,最后有一个类似的匹配表达式与实际的listMethod代码,它使用我创建的几个临时变量varDecl.

我现在的问题是:如何使上述代码更紧凑?

我希望匹配所有具有2个参数(类型string和类型Expression)的类型,而不是自己列出它们,这有点丑陋且难以维护.

ListMethod类型声明如下(整个事情是一个FsLex/FsYacc项目):

type ListMethod =
    | Select of string * Expr
    | Where of string * Expr
    | Sum of string * Expr
    | Concat of string * Expr
    | ...
    | somethingElse of Expr
Run Code Online (Sandbox Code Playgroud)

(截至目前我只有表格的类型string * Expr,但会改变).

我认为对于任何有经验的人来说,这是一个相当愚蠢的问题,但正如我所说,我是F#的新手并且自己无法找到解决方案.

提前致谢!

编辑: 我真的想避免列出所有可能类型的listMethod两次.如果我无法在match表达式中使用通配符或占位符,也许我可以修改listMethod类型以使事情更清晰.

想到的一个选项是仅listMethod创建一种类型并为具体类型创建第三个参数(Select,Where,Sum).还是有更好的方法?

Ram*_*nir 7

这可能是标准方式:

let (varDecl, listExpr) =
    match listMethod with 
    | Select (var, expr)
    | Where (var, expr)
    | Sum (var, expr)
    | Concat (var, expr) -> (var, expr)
Run Code Online (Sandbox Code Playgroud)

|标志的含义or,因此,如果这些比赛之一,其结果将被退回.只需确保每个案例都具有完全相同的名称(和类型).

正如Chuck评论的那样,这是一个更好的解决方案:

let (Select (varDecl, expr)
    | Where (varDecl, expr)
    | Sum (varDecl, expr)
    | Concat (varDecl, expr)) = listMethod
Run Code Online (Sandbox Code Playgroud)


Jon*_*rop 5

我认为对于任何有经验的人来说,这是一个相当愚蠢的问题,但正如我所说,我是F#的新手并且自己无法找到解决方案.

相反,这是一个非常好的问题,实际上是相对不受约束的,因为F#在这方面与其他语言不同(例如,你可以使用OCaml中的多态变体来解决这个问题).

正如Ankur所写,最好的解决方案始终是改变您的数据结构,以便在可能的情况下更轻松地完成您需要做的事情.KVB使用活动模式的解决方案不仅有价值而且新颖,因为该语言功能在其他语言中并不常见.Ramon建议使用or-patterns组合你的匹配案例也很好,但是你不想编写不完整的模式匹配.

也许在实践中出现的这个问题最常见的例子是运营商:

type expr =
  | Add of expr * expr
  | Sub of expr * expr
  | Mul of expr * expr
  | Div of expr * expr
  | Pow of expr * expr
  | ...
Run Code Online (Sandbox Code Playgroud)

您可以在哪里重构您的类型,如下所示:

type binOp = Add | Sub | Mul | Div | Pow

type expr =
  | BinOp of binOp * expr * expr
  | ...
Run Code Online (Sandbox Code Playgroud)

然后提取子表达式等任务:

let subExprs = function
  | Add(f, g)
  | Sub(f, g)
  | Mul(f, g)
  | Div(f, g)
  | Pow(f, g) -> [f; g]
  | ...
Run Code Online (Sandbox Code Playgroud)

可以更容易地执行:

let subExprs = function
  | BinOp(_, f, g) -> [f; g]
  | ...
Run Code Online (Sandbox Code Playgroud)

最后,不要忘记您可以使用OOP构造(例如实现共享接口)来扩充F#类型(例如union类型).这也可用于表达共性,例如,如果您对两种类型有两个重叠的要求,那么您可以使它们都实现相同的接口以暴露这种共性.