OCaml:在另一个expression式中匹配expression式?

我目前正在与OCaml一起开展一个小型项目。 一个简单的mathexpression式简化器。 我应该在expression式中find某些模式,并简化它们,以减lessexpression式中括号的数量。 到目前为止,我已经能够实现除了两个以外的大多数规则,为此我决定创build一个recursion的,模式匹配的“filter”函数。 我需要实现的两个规则是:

– 将formsa – (b + c)或类似forms的所有expression式转换为a – b – c

– 将a /(b * c)或类似forms的所有expression式转换为a / b / c

…我怀疑是相当简单的,一旦我设法实现一个,我可以很容易地实现其他。 但是,我遇到了recursion模式匹配function的麻烦。 我的typesexpression是这样的:

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 *) ;; 

而我主要遇到的麻烦是在比赛performance。 例如,我正在尝试这样的事情:

 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 ;; 

然而,标志线上的匹配expression式似乎被认为是前一个“内部匹配”的一部分,而不是“主要匹配”,因此所有“Quot(…)”expression式都不会被识别。 是否有可能在这样的其他匹配expression式中有匹配expression式? 结束内部匹配的正确方法是什么?所以我可以继续匹配其他的可能性?

忽略逻辑,因为它几乎是我第一次想到的,只是因为我必须首先处理这个“匹配”错误,所以我一直无法尝试,尽pipe任何有关如何处理recursion的build议或这个逻辑将是受欢迎的。

快速解决scheme

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

 让recfilterexp =
    与exp匹配
     |  Var v  - > Var v
     |  Sum(e1,e2) - > Sum(e1,e2)
     |  Prod(e1,e2) - > Prod(e1,e2)
     |  Diff(e1,e2) - >
             与e2搭配
              |  Sum(e3,e4) - > filter(diffRule e2)
              |  Diff(e3,e4) - > filter(diffRule e2)
              |  _  - >filtere2 
     |  Quot(e1,e2) - >
             (与e2搭配
              |  Quot(e3,e4) - > filter(quotEule e2)
              |  Prod(e3,e4) - > filter(“Rule e2)
              |  _  - >filtere2)
 ;;

简化

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

 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 ;; 

通过用_ (下划线)replace未使用的模式variables,可以使其更具可读性。 这也适用于整个子模式,如(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 ;; 

最后,你可以用e2replacee2 ,并用function快捷键replacematch

 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的模式语法很好,不是吗?

通过明智地使用下划线,as和or-patterns,你可以使这个terser(我会更清楚地辩论)。 由此产生的代码也更有效率,因为它分配更less(在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 ;;