在SML中匹配两个自定义数据类型

时间:2017-12-14 15:12:16

标签: pattern-matching sml ml

我有两种自定义数据类型

datatype expression = Constant of int
                    | Variable of string
                    | Operator of string * expression
                    | Pair of expression list
                    | List of expression list

datatype pattern = ConstantP of int
                 | VariableP of string
                 | OperatorP of string * pattern
                 | PairP of pattern list
                 | ListP of pattern list
                 | UnorderedListP of pattern list
                 | Wildcard

我应该实现一个功能

match (fn : expression * pattern -> (string * expression) list option) 

获取表达式和模式。如果匹配,则返回SOME (list of bindings),否则返回NONE

匹配应该以这种方式进行:

匹配是在表达式的元组和列表(表达式*模式)上定义的。表达式和模式可以匹配与否。如果他们这样做,匹配产生一个绑定列表 - 名称和值对((字符串*表达式)列表),列表的顺序无关紧要。匹配使用以下规则:

  • 通配符匹配任何内容并生成一个空的绑定列表。
  • VariableP s匹配任何值v并返回带有一个绑定(s,v)的列表。每个变量名称s在每个模式中最多只出现一次。
  • ConstantP 42 仅匹配常量42并生成空的绑定列表。对于其余的整数,它的工作方式相同。
  • PairP [a1,a2] 匹配表达式对[b1,b2]如果a1与b1匹配且a2与b2匹配。匹配产生两个匹配的连接列表。
  • ListP ps 匹配表达式List xs,如果xs中的每个值都与ps中的相应模式匹配。匹配产生一个列表,我们通过连接所有匹配接收的绑定来获得。
  • 如果运算符a和b的名称相同且“x”与“y”匹配,则OperatorP(a,x)匹配表达式Operator(b,y)。匹配产生了我们通过匹配x和y获得的绑定列表。

如果你能给我一些方向的提示, 我认为我的功能应该是这样的,我猜,但不确定是否会以递归的方式覆盖所有情况

fun match(e: expression, p: pattern) : (string * expression) list option =
    case p of 
         Wildcard => SOME []
       | VariableP s => ...

1 个答案:

答案 0 :(得分:2)

使用更多模式匹配。
然后逐个浏览规则列表并翻译它们,当规则依赖于子匹配时递归 您只需要定义匹配构造函数的情况,留下最后一个默认情况下的不匹配。

fun match (_, Wildcard) = SOME []
  | match (Constant x, ConstantP y) = if x = y then SOME [] else NONE
  | match (Pair [p1, p2], PairP [p1', p2']) = 
                                  let 
                                      val match1 = match (p1, p1')
                                  in case match1 of
                                         NONE => NONE
                                       | SOME ls => ...
                                  end
  | ...
  | match _ = NONE 

但是你可以通过定义一些辅助函数来使事情变得更方便 (一旦你输入所有内容,上面的Pair子句就会变得笨拙。)

例如,只有当两个可选列表都不是NONE时才能将它们连接成一个函数非常简洁:

fun appendOptional (SOME xs, SOME ys) = SOME (xs @ ys)
  | appendOptional _ = NONE

让你写一行

  | match (Pair[p1, p2], PairP[p1', p2']) = appendOptional(match(p1, p1'), match(p2, p2'))

并且该功能在其他情况下也可以派上用场 (如果没有帮助函数,列表规则很难写,如果不是不可能的话。)

更详细的例子:

datatype expression =  Constant of int
                     | Variable of string
                     | Pair of expression list

datatype pattern = ConstantP of int
                 | VariableP of string
                 | PairP of pattern list
                 | Wildcard

fun appendOptional (SOME xs, SOME ys) = SOME (xs @ ys)
  | appendOptional _ = NONE

fun match (_, Wildcard) = SOME []
  | match (Constant x, ConstantP y) = if x = y then SOME [] else NONE
  | match (Pair [p1, p2], PairP [p1', p2']) = appendOptional (match (p1, p1'), match (p2, p2'))
  | match (e, VariableP v) = SOME [(v, e)] 
  | match _ = NONE 

测试:

- match (Constant 32, ConstantP 1);
val it = NONE : (string * expression) list option

- match (Constant 32, ConstantP 32);
val it = SOME [] : (string * expression) list option

- match (Constant 32, VariableP "x");
val it = SOME [("x",Constant 32)] : (string * expression) list option

- match (Pair [Constant 32, Variable "y"], PairP [VariableP "a", VariableP "b"]);
val it = SOME [("a",Constant 32),("b",Variable "y")]
  : (string * expression) list option