OCaml:在另一个内部匹配表达式?

Posted

技术标签:

【中文标题】OCaml:在另一个内部匹配表达式?【英文标题】:OCaml: Match expression inside another one? 【发布时间】:2010-09-20 10:23:13 【问题描述】:

我目前正在使用 OCaml 进行一个小项目;一个简单的数学表达式简化器。我应该在表达式中找到某些模式,并对其进行简化,以减少表达式中括号的数量。到目前为止,我已经能够实现除了两个规则之外的大多数规则,为此我决定创建一个递归的、模式匹配的“过滤器”函数。我需要执行的两条规则是:

-将a - (b + c) 或类似形式的所有表达式转换为a - b - c

-将a / (b * c)或类似形式的所有表达式转换为a / b / c

...我怀疑这会相当简单,一旦我设法实现了一个,我就可以轻松地实现另一个。但是,我在使用递归模式匹配函数时遇到了问题。我的类型表达是这样的:

type expr =
 | Var of string            (* variable *)
 | Sum of expr * expr       (* sum  *)
 | Diff of expr * expr      (* difference *)
 | Prod of expr * expr      (* product *)
 | Quot of expr * expr      (* quotient *)
;;

而我主要遇到的问题是匹配表达式。例如,我正在尝试这样的事情:

let rec filter exp =   
    match exp with       
    | Var v -> Var v                        
    | Sum(e1, e2) -> Sum(e1, e2)          
    | Prod(e1, e2) -> Prod(e1, e2)
    | Diff(e1, e2) ->
        match e2 with
        | Sum(e3, e4) -> filter (diffRule e2)
        | Diff(e3, e4) -> filter (diffRule e2)      
        | _ -> filter e2         
    | Quot(e1, e2) ->                                 ***this line***
        match e2 with  
        | Quot(e3, e4) -> filter (quotRule e2)        
        | Prod(e3, e4) -> filter (quotRule e2)        
        | _ -> filter e2
;;

但是,标记行上的匹配表达式似乎被识别为先前“内部匹配”而不是“主匹配”的一部分,因此永远不会识别所有“Quot(...)”表达式.甚至可以在像这样的其他匹配表达式中包含匹配表达式吗?结束内部匹配的正确方法是什么,以便我可以继续匹配其他可能性?

忽略逻辑,因为这几乎是我首先想到的,只是我无法尝试,因为我必须首先处理这个“匹配”错误,尽管关于如何处理的任何建议递归或逻辑将受到欢迎。

【问题讨论】:

【参考方案1】:

快速解决方案

您只需要在内部匹配周围添加括号或begin/end

let rec filter exp =
    match exp with
    | Var v -> Var v
    | Sum (e1, e2) -> Sum (e1, e2)
    | Prod (e1, e2) -> Prod (e1, e2)
    | Diff (e1, e2) ->
            (match e2 with
             | Sum (e3, e4) -> filter (diffRule e2)
             | Diff (e3, e4) -> filter (diffRule e2)
             | _ -> filter e2)
    | Quot (e1, e2) ->
            (match e2 with
             | Quot (e3, e4) -> filter (quotRule e2)
             | Prod (e3, e4) -> filter (quotRule e2)
             | _ -> filter e2)
;;

简化

在您的特定情况下,不需要嵌套匹配。 您可以使用更大的模式。您还可以使用“|”(“或”)模式消除嵌套规则中的重复:

let rec filter exp =
    match exp with
    | Var v -> Var v
    | Sum (e1, e2) -> Sum (e1, e2)
    | Prod (e1, e2) -> Prod (e1, e2)
    | Diff (e1, (Sum (e3, e4) | Diff (e3, e4) as e2)) -> filter (diffRule e2)
    | Diff (e1, e2) -> filter e2
    | Quot (e1, (Quot (e3, e4) | Prod (e3, e4) as e2)) -> filter (quotRule e2)
    | Quot (e1, e2) -> filter e2
;;

您可以通过将未使用的模式变量替换为_(下划线)来使其更具可读性。 这也适用于整个子模式,例如 (e3,e4) 元组:

let rec filter exp =
    match exp with
    | Var v -> Var v
    | Sum (e1, e2) -> Sum (e1, e2)
    | Prod (e1, e2) -> Prod (e1, e2)
    | Diff (_, (Sum _ | Diff _ as e2)) -> filter (diffRule e2)
    | Diff (_, e2) -> filter e2
    | Quot (_, (Quot _ | Prod _ as e2)) -> filter (quotRule e2)
    | Quot (_, e2) -> filter e2
;;

以同样的方式,您可以继续简化。比如前三种情况(VarSumProd)原样返回,可以直接表示:

let rec filter exp =
    match exp with
    | Var _ | Sum _ | Prod _ as e -> e
    | Diff (_, (Sum _ | Diff _ as e2)) -> filter (diffRule e2)
    | Diff (_, e2) -> filter e2
    | Quot (_, (Quot _ | Prod _ as e2)) -> filter (quotRule e2)
    | Quot (_, e2) -> filter e2
;;

最后,您可以将e2 替换为e 并将match 替换为function 快捷方式:

let rec filter = function
    | Var _ | Sum _ | Prod _ as e -> e
    | Diff (_, (Sum _ | Diff _ as e)) -> filter (diffRule e)
    | Diff (_, e) -> filter e
    | Quot (_, (Quot _ | Prod _ as e)) -> filter (quotRule e)
    | Quot (_, e) -> filter e
;;

OCaml 的模式语法很好,不是吗?

【讨论】:

【参考方案2】:

您可以通过明智地使用下划线、as 和 or 模式来使这个更简洁(我认为更清晰)。生成的代码也更高效,因为它分配的更少(在 Var、Sum 和 Prod 情况下)

let rec filter = function
| Var _ | Sum _ | Prod _ as e -> e
| Diff (_, (Sum _ | Diff _) as e) -> filter (diffRule e)
| Diff (_,e) -> e
| Quot (_, (Quot _| Prod _) as e) -> filter (quoteRule e)
| Quot (_,e) -> filter e
;;

【讨论】:

此解决方案无效。 错误:构造函数 Diff 需要 2 个参数,但在此处应用于 1 个参数

以上是关于OCaml:在另一个内部匹配表达式?的主要内容,如果未能解决你的问题,请参考以下文章

Ocaml模式匹配“方形”元组?

Ocaml 类型不匹配?预期单位,但为已定义类型

用 let 重建 OCaml 模式

OCaml 表达式类型问题

在 OCaml 中分隔多个“let”声明,后跟一个“let in”表达式

OCaml 断言失败