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

发布于 2024-12-02 02:06:24 字数 1398 浏览 8 评论 0原文

我是 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 类型以使事情变得更清晰。

我想到的一个选项是仅创建 1 种类型的 listMethod 并为具体类型创建第三个参数(Select、Where、Sum)。 或者有更好的方法吗?

I'm new to F# and not quite familiar with the whole pattern matching idea.
I tried to search for a better solution to my problem but I fear I can't even express the problem properly – I hope the question title is at least somewhat accurate.

What I want to do is extract 2 "parameters" from listMethod.
listMethod is of one of several types that have a string and an Expression "parameter" (I suspect parameter is the wrong term):

    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)

Then I continue to work with varDecl and at the end have a similar match expression with the actual listMethod code that makes use of several temporary variables I created based on varDecl.

My question now is: How can I make the above code more compact?

I want to match all those types that have 2 parameters (of type string and Expression) without listing them all myself, which is kinda ugly and hard to maintain.

The ListMethod type is declared as follows (the whole thing is a FsLex/FsYacc project):

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

(as of now I only have types of the form string * Expr, but that will change).

I reckon that this is a fairly dumb question for anyone with some experience, but as I've said I'm new to F# and couldn't find a solution myself.

Thanks in advance!

Edit: I'd really like to avoid listing all possible types of listMethod twice. If there's no way I can use wildcards or placeholders in the match expressions, perhaps I can modify the listMethod type to make things cleaner.

One option that comes to mind would be creating only 1 type of listMethod and to create a third parameter for the concrete type (Select, Where, Sum).
Or is there a better approach?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

狼性发作 2024-12-09 02:06:24

这可能是标准方式:

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

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

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

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

This is probably the standard way:

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

The | sign means or, so if one of these match, the result will be returned. Just make sure that every case has exactly the same names (and types).

As Chuck commented, this is an even better solution:

let (Select (varDecl, expr)
    | Where (varDecl, expr)
    | Sum (varDecl, expr)
    | Concat (varDecl, expr)) = listMethod
蓝天 2024-12-09 02:06:24

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

相反,这是一个非常好的问题,而且实际上是相对未涉足的领域,因为 F# 在这方面与其他语言不同(例如,您可以使用 OCaml 中的多态变体来解决这个问题)。

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

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

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]
  | ...

:最后,不要忘记您可以增强 F# 类型(例如作为联合类型)与 OOP 构造(例如实现共享接口)。这也可以用来表达共性,例如,如果您对两种类型有两个重叠的需求,那么您可以让它们都实现相同的接口,以便公开这种共性。

I reckon that this is a fairly dumb question for anyone with some experience, but as I've said I'm new to F# and couldn't find a solution myself.

On the contrary, this is a very good question and actually relatively untrodden ground because F# differs from other languages in this regard (e.g. you might solve this problem using polymorphic variants in OCaml).

As Ankur wrote, the best solution is always to change your data structure to make it easier to do what you need to do if that is possible. KVB's solution of using active patterns is not only valuable but also novel because that language feature is uncommon in other languages. Ramon's suggestion to combine your match cases using or-patterns is also good but you don't want to write incomplete pattern matches.

Perhaps the most common example of this problem arising in practice is in operators:

type expr =
  | Add of expr * expr
  | Sub of expr * expr
  | Mul of expr * expr
  | Div of expr * expr
  | Pow of expr * expr
  | ...

where you might restructure your type as follows:

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

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

Then tasks like extracting subexpressions:

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

can be performed more easily:

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

Finally, don't forget that you can augment F# types (such as union types) with OOP constructs such as implementing shared interfaces. This can also be used to express commonality, e.g. if you have two overlapping requirements on two types then you might make them both implement the same interface in order to expose this commonality.

北座城市 2024-12-09 02:06:24

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

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

设计数据结构时应牢记要对其执行的操作。

In case you are ok to do adjustments to your data structure then below is something that will ease out the pattern matching.

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

A data structure should be designed by keeping in mind the operation you want to perform on it.

叹倦 2024-12-09 02:06:24

如果有时您希望对所有案例进行相同的处理,而有时您希望根据是否处理 SelectWhereSum 等,那么一种解决方案是使用活动模式:

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

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

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

If there are times when you will want to treat all of your cases the same and other times where you will want to treat them differently based on whether you are processing a Select, Where, Sum, etc., then one solution would be to use an active pattern:

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

Now you can still match normally if you need to treat the cases individually, but you can also match using the active pattern:

let varDecl, listExp = 
    match listMethod with
    | OperatorExpression(_, v, e) -> v, e
    | _ -> // whatever you do for other cases...
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文