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

时间:2011-08-30 07:26:29

标签: f# pattern-matching fsyacc

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

我想要做的是从listMethod中提取2个“参数”。 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)

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

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

我希望匹配所有具有2个参数(类型为stringExpression)的类型,而不是自己列出这些类型,这有点丑陋且难以维护。

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

type ListMethod =
    | Select of string * Expr
    | Where of string * Expr
    | Sum of string * Expr
    | Concat of string * Expr
    | ...
    | somethingElse of Expr

(截至目前我只有string * Expr形式的类型,但会改变。)

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

提前致谢!

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

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

4 个答案:

答案 0 :(得分:7)

这可能是标准方式:

let (varDecl, listExpr) =
    match listMethod with 
    | Select (var, expr)
    | Where (var, expr)
    | Sum (var, expr)
    | Concat (var, expr) -> (var, expr)

|符号表示or,因此如果其中一个匹配,则返回结果。只需确保每个案例都具有完全相同的名称(和类型)。

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

let (Select (varDecl, expr)
    | Where (varDecl, expr)
    | Sum (varDecl, expr)
    | Concat (varDecl, expr)) = listMethod

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

您可以按照以下方式重新构建类型:

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

type expr =
  | BinOp of binOp * expr * expr
  | ...

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

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

可以更轻松地执行:

let subExprs = function
  | BinOp(_, f, g) -> [f; g]
  | ...

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

答案 2 :(得分:4)

如果您可以对数据结构进行调整,那么下面的内容可以简化模式匹配。

type ListOperations = 
    Select | Where | Sum | Concat


type ListMethod =
    | ListOp of ListOperations * string * Expr
    | SomethingElse of int

let test t = 
    match t with
    | ListOp (a,b,c) -> (b,c)
    | _ -> ....

应该通过记住要对其执行的操作来设计数据结构。

答案 3 :(得分:3)

如果有时您希望根据您处理的是SelectWhere,{Sumlet (|OperatorExpression|_|) = function | Select(var, expr) -> Some(Select, var, expr) | Where (var, expr) -> Some(Where, var, expr) | Sum (var, expr) -> Some(Sum, var, expr) | Concat (var, expr) -> Some(Concat, var, expr) | _ -> None ,{ {1}}等,然后一种解决方案是使用活动模式:

let varDecl, listExp = 
    match listMethod with
    | OperatorExpression(_, v, e) -> v, e
    | _ -> // whatever you do for other cases...

现在,如果您需要单独处理案例,您仍然可以正常匹配,但您也可以使用活动模式进行匹配:

{{1}}