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 ;;
以同样的方式,你可以进行简化。 例如,前三个例子( Var
, Sum
, Prod
)不加修改地返回,您可以直接表示:
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
replacee2
,并用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 ;;