SML不排序判断两无序列表元素频次相等的代码报错排查
问题说明
需求为判断两个无序列表是否包含完全相同的值,且各值出现次数完全一致,作业明确要求不得对列表进行排序。
原实现采用递归比较逻辑:
- 以两列表长度不同为基准情况,直接返回
false - 每次递归检查第二个列表
l2是否包含第一个列表l1的首元素hd:- 若存在,移除
l1首元素、l2中第一个匹配到的同值元素,对剩余部分递归调用isEqual - 若不存在,直接返回
false
- 若存在,移除
原实现代码
fun doesExist l = List.exists (fn x => x = true) l ; (* returns true if there's at least one 'true' in the list of bool *) fun getFirstIndex target (a::b) index = if (a::b) = [] then index else if target = a then index else getFirstIndex target b index+1 ; (* returns the index of the first element = to 'target', in this case 'true', looking into the list (a::b) *) fun delete (_, nil) = nil | delete (0, _::xs) = xs | delete (i, x::xs) = x::delete(i-1,xs) ; (* should delete an element from a list, given the index gotten with the function above *) fun isEqual [] [] = true | isEqual [] _ = false | isEqual _ [] = false | isEqual _ _ = false | isEqual l1 l2 = if List.length l1 <> List.length l2 then false else if doesExist(List.map (fn n => n = hd l1) l2) then isEqual(tl l1, delete(getFirstIndex(true, l2, 0), l2)) else false ; (* this function puts altogether and should return either false or true *)
预期测试用例输出
isEqual [] []; (* true *) isEqual [1] [1]; (* true *) isEqual [1,4,2,8] [8,1,4,2]; (* true *) isEqual [1,2,4,3] [11,24,56,7]; (* false *) isEqual [7,5,12,88] [7,88,12,5,5]; (* false *) isEqual [7,5,12,88,88] [7,88,12,5,5]; (* false *) isEqual [7,5,12,88] [7,5,12,88,13,15]; (* false *)
编译报错信息
error: Type error in function application. Function: delete : int * 'a list -> 'a list Argument: (getFirstIndex (true, l2, 0), l2) : ((bool * ''a list * int) list -> int -> int) * ''a list Reason: Can't unify int to (bool * ''a list * int) list -> int -> int (Incompatible types) Found near if doesExist (List.map (fn ... => ...) l2) then isEqual (tl l1, delete (...)) else false es13.sml:24: error: Type of function does not match type of recursive application. Function: fun isEqual [] [...] = true | isEqual [...] ... = false | isEqual ... = false | isEqual ... = ... | ... : ''a list -> ''a list -> bool Variable: isEqual : ''a list * 'b list -> bool Reason: Can't unify ''a list to ''a list * 'b list (Incompatible types) Found near fun isEqual [] [...] = true | isEqual [...] ... = false | isEqual ... = false | isEqual ... = ... | ... Exception- Fail "Static Errors" raised
问题排查与修复
代码共存在5处问题:
- SML函数调用语法错误:SML中柯里化函数传参不需要将参数打包为元组,原代码递归调用
isEqual(tl l1, delete(...))相当于给isEqual传入一个二元元组,和定义的“接收两个独立列表参数”的签名不匹配,触发类型错误。 - 运算符优先级错误:
getFirstIndex target b index+1中函数调用优先级高于算术运算符,实际执行逻辑为(getFirstIndex target b index) + 1,并非预期的传入index+1作为新索引值,需要给index+1加括号修正优先级。 getFirstIndex参数传递错误:该函数为柯里化定义,第一个参数为查找目标值,原代码调用时写成getFirstIndex(true, l2, 0)把三个参数打包为元组传入,完全不匹配函数签名;且原逻辑先通过List.map生成布尔列表再查找true属于冗余逻辑,可直接查找目标元素。- 分支顺序错误:
isEqual _ _ = false写在通用递归分支之前,所有非空双列表输入都会直接匹配该分支返回false,后续递归逻辑永远无法执行。 - 边界匹配缺失:
getFirstIndex仅匹配了非空列表构造a::b,传入空列表时会触发匹配非穷尽错误。
修复后的完整可运行代码如下,全程未对原列表排序,符合要求:
(* 查找目标元素在列表中第一次出现的索引,不存在返回~1 *) fun getFirstIndex target nil index = ~1 | getFirstIndex target (a::b) index = if target = a then index else getFirstIndex target b (index + 1) (* 删除列表指定索引位置的元素,索引非法时返回原列表 *) fun delete (_, nil) = nil | delete (0, _::xs) = xs | delete (i, x::xs) = if i < 0 then x::xs else x::delete(i-1, xs) fun isEqual [] [] = true | isEqual [] _ = false | isEqual _ [] = false | isEqual l1 l2 = if List.length l1 <> List.length l2 then false else let val target = hd l1 val idx = getFirstIndex target l2 0 in if idx = ~1 then false else isEqual (tl l1) (delete (idx, l2)) end
上述代码可全部通过列出的测试用例。
内容的提问来源于stack exchange,提问作者Dovday
相关产品推荐
相关产品推荐

