我是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个参数(类型为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
(截至目前我只有string * Expr
形式的类型,但会改变。)
我认为对于任何有经验的人来说,这是一个相当愚蠢的问题,但正如我所说,我是F#的新手并且自己找不到解决方案。
提前致谢!
修改我真的想避免两次列出所有可能类型的listMethod
。如果我无法在match
表达式中使用通配符或占位符,也许我可以修改listMethod
类型以使事情更清晰。
我想到的一个选项是只创建一种listMethod
类型,并为具体类型创建第三个参数(Select,Where,Sum)。
还是有更好的方法?
答案 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)
如果有时您希望根据您处理的是Select
,Where
,{Sum
,let (|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}}