SML编写模式匹配函数时报非构造子应用于模式的问题排查
报错核心根因
你排查的方向完全正确,这个报错就是因为你在模式匹配的分支里误用了非构造器的普通变量。
SML的模式匹配有强制规则:出现在模式位置的所有标识符,只有内置构造器、字面量、自定义的datatype构造器是合法的匹配常量,所有普通变量(不管是外层函数形参、局部绑定的变量)都会被默认当成新的占位绑定,不会去读取它的实际值做等值匹配。
你代码里的具体问题有两个:
- 你写在
legal分支模式里的n、m是外层illegal函数传入的普通形参,不是合法的模式构造器,解析器识别到你试图把它当模式常量用,就抛出了non-constructor applied to argument in pattern的报错。 - 就算不考虑报错,你原来的写法逻辑也是错位的:你的
legal函数只有3个形参(字符c、整数i、整数j),你写legal (#"C") i n时把n放在了第三个形参j的匹配位置,legal (#"D") m j把m放在了第二个形参i的匹配位置,哪怕不报错,也完全实现不了你想要的「C字符时i等于外层传入的n、D字符时i等于外层传入的m」的判断逻辑。
修正方案
如果需要把外层传入的普通变量值作为匹配判断条件,绝对不能直接把变量名写在模式位置,必须把值的判断逻辑挪到分支的表达式部分,或者用卫语句实现。
对应你原本想要的逻辑(A字符且i=0合法、B字符且j=0合法、C字符且i等于外层n合法、D字符且i等于外层m合法,其余情况返回false),修正后的可运行代码如下:
fun illegal (c:char) i j n m = let fun legal (#"A") 0 _ = true (* A匹配:i为0时合法,j取值不影响判断 *) | legal (#"B") _ 0 = true (* B匹配:j为0时合法,i取值不影响判断 *) | legal (#"C") cur_i _ = (cur_i = n) (* C匹配:取出i的实际值,和外层传入的n比较 *) | legal (#"D") cur_i _ = (cur_i = m) (* D匹配:取出i的实际值,和外层传入的m比较 *) | legal _ _ _ = false (* 其余所有情况都不合法 *) in if legal c i j then i else 0 end
容易踩的同类坑提醒
- 为啥你写的
#"A"、0不会触发报错?因为前者是char类型的字面量构造器,后者是int类型的零值字面量,属于SML允许直接写在模式位置的合法匹配项。 - 哪怕你是在同一个let块里用
val绑定的普通值,只要不是datatype定义的构造器,写在模式位置都会被当成新的占位绑定,不会读取它的实际值。比如val k = 3之后写模式k,根本不是匹配值3,只是创建了一个叫k的新绑定。 - 原代码里
legal c i j = false的写法没必要,布尔值可以直接作为if的判断条件,不需要额外和false做等值比较。
内容的提问来源于stack exchange,提问作者tonythestark
相关产品推荐
相关产品推荐

