模式匹配返回数学表达式的字符串表示

时间:2010-11-09 14:37:03

标签: functional-programming ocaml

我必须编写一个带有表达式

的函数转储
type expression = 
| Int of int
| Float of float
| Add of expression * expression
| Sub of expression * expression
| Mult of expression * expression
| Div of expression * expression
;;

并返回它的字符串表示形式。 例如:

dump (Add (Int 1, Int 2));;
dump (Mult (Int 5, Add(Int 2, Int 3)), Int 1)

应分别返回

- : string = "1+2"
- : string = "5*(2+3)-1"

我写过这样的话:

let rec dump e = match e with
    | Int a -> string_of_int a
    | Float a -> string_of_float a
    | Add (e1,e2) -> "(" ^ (dump e1) ^ "+" ^ (dump e2) ^ ")"
    | Sub (e1,e2) -> "(" ^ (dump e1) ^ "-" ^ (dump e2) ^ ")"
    | Mult (e1,e2) -> (dump e1) ^ "*" ^ (dump e2)
    | Div (e1,e2) -> (dump e1) ^ "/" ^ (dump e2)
;;

并且返回的表达式是正确的,但仍然不是最佳的。 (对于Add(Int 1,Int 2)),它是(1 + 2)并且应该是1 + 2)。我怎样才能解决这个问题? (没有嵌套模式匹配,这不是一个好主意)

4 个答案:

答案 0 :(得分:4)

让我们考虑一下你需要什么时候:

首先,总是围绕某些操作包装parens是错误的方法。术语是否需要括号不仅取决于术语中使用哪个运算符,还取决于术语是哪个运算符的操作数。

E.g。当1+23+4+的操作数时,它应该是1+2+3+4 - 没有parens。但是,如果运算符为*,则需要(1+2) * (3+4)

那么我们需要哪些运营商组合呢?

+的操作数永远不需要用括号括起来。如果操作数是产品或商,则它们具有更高的优先级,如果操作数是差异,则不需要parens,因为x + (y - z) = x + y -z

-它有点不同。 */仍然不需要括号,因为它们具有更高的优先级,但+-如果它们位于第二个操作数中,则{{1} },但是x + y - z = (x + y) - z

对于Mult,如果它们是Add或Sub,则两个操作数都需要加括号,但如果它们是Mult或Div则不需要括号。

使用Div,第一个操作数需要加上括号,如果它是Add或Sub,第二个操作数总是需要括号(除非它是Int或Float,当然)。

答案 1 :(得分:3)

首先,为运营商定义优先级列表:

module Prio = struct
  let div = 4
  let mul = 3
  let sub = 2
  let add = 1
end

一个有用的结构是“如果条件为真,则用括号括起来”:

let wrap_if c str = if c then "("^str^")" else str

最后,定义一个辅助打印功能,它提供了一个“优先级”参数,意思是“顺便说一句,你包含在一个具有优先级X的表达式中,因此保护你的输出”:

let dump e = 
  let rec aux prio = function
    | Int a -> string_of_int a
    | Float a -> string_of_float a
    | Add (e1,e2) -> 
        wrap_if (prio > Prio.add) (aux Prio.add e1 ^ "+" ^ aux Prio.add e2)
    | Sub (e1,e2) -> 
        wrap_if (prio > Prio.add) (aux Prio.add e1 ^ "-" ^ aux Prio.sub e2)
    | Mult (e1,e2) -> 
        wrap_if (prio > Prio.mul) (aux Prio.mul e1 ^ "*" ^ aux Prio.mul e2)
    | Div (e1,e2) -> 
        wrap_if (prio > Prio.mul) (aux Prio.mul e1 ^ "/" ^ aux Prio.div e2)
  in aux Prio.add e
;;

答案 2 :(得分:2)

听起来我想要构建一些减少规则集,这些规则可以应用于根据操作顺序产生表达式的“美化”或最简化形式,例如:交换性,结合性等。例如(a + a) => a + a(a * b) + c => a * b + c等等。

答案 3 :(得分:2)

一个相当简单且相当通用的答案(适用于除数学表达式之外的其他语法):为构造函数选择优先级(以及,如果你挑剔,关联性),并且只有当子项构造函数的优先级低于子构造函数时才添加括号当前的构造函数。

更准确地说:当你想要打印构造函数C(x1,x2,x3..)时,你会看到每个xi的头部构造函数(如果x1D(y1,y2..),它的头部构造函数是D),比较CD的优先级。如果D的优先级较低,则在x2的字符串表示周围添加括号。