SML错误:if分支的类型不一致

SML错误:if分支的类型不一致,sml,smlnj,Sml,Smlnj,我试图找出一个元素是否是集合的一部分。以下是我的功能: fun elementOf(x:int, (nil:int list, nil:bool list)) = nil | elementOf(x, (y::ys, z::zs)) = if x = y then z else elementOf(x, (ys, zs)); 因此,如果我调用elementOf(2,([2,7,4],[false,true,false])它将返回false 但是,我收到了错误消息: Error: types

我试图找出一个元素是否是集合的一部分。以下是我的功能:

fun elementOf(x:int, (nil:int list, nil:bool list)) = nil
  | elementOf(x, (y::ys, z::zs)) = if x = y then z else elementOf(x, (ys, zs));
因此,如果我调用
elementOf(2,([2,7,4],[false,true,false])
它将返回
false

但是,我收到了错误消息:

Error: types of if branches do not agree [tycon mismatch]
then branch: bool
else branch: 'Z list
in expression: if x = y then z else elementOf(x,ys,zs))
什么是'Z列表?如何修复此错误

什么是“Z”列表

“Z列表”是标准ML在基本情况下推断
nil
返回类型的方法
nil
可以是任何类型的列表,因为它是空的。”Z是一个类型变量

如何修复此错误

问题是
elementOf
有时不能返回
nil
,而其他时间则不能返回
true
/
false
。通过将基本情况更改为
false
,该功能将工作,以使列表中不包含的元素也不包含在集合中:

fun elementOf(x:int, (nil:int list, nil:bool list)) = false
  | elementOf(x, (y::ys, z::zs)) = if x = y then z else elementOf(x, (ys, zs));
但是,可能没有一个很好的理由来存储不在集合中的元素,而不是将它们扔掉。一个稍微有效一点的代表只是一份成员名单:

fun elementOf (x, nil : int list) = false
  | elementOf (x, y::ys) = x = y orelse elementOf (x, ys)
或使用内置列表组合器创建。存在:

fun elementOf x xs = List.exists (y => x = y) xs
或写在:

更好的方法是使用二叉树:

data binTree = Empty
             | Node of binTree * int * binTree

fun elementOf (x, Empty) false
  | elementOf (x, Node (left, y, right)) =
    (case Int.compare (x, y) of
          LESS => elementOf (x, left)
        | EQUAL => true
        | GREATER => elementOf (x, right))

编译器已确定“then”分支必须返回类型
bool
,但“else”分支必须返回类型
'Z list
。类型
'Z list
表示“元素可以是任何东西的列表”。以
开头的类型是类型变量,如果编译器确定不是所有类型都可以,则可以稍后用另一个类型替换,或者如果类型可以是多态的,则可能最终保留该类型

“then”分支必须是一个布尔值,因为它是
z
,在匹配构造中给定的模式是上面模式指定的类型为
bool list
的列表的第一个元素。“else”分支必须是一个列表,因为它是对
elementOf
的调用,模式匹配的第一种情况是
elementOf
返回
nil
,这是一个列表(对元素类型没有约束:因为空列表没有元素,所以它的元素可以说是您想要选择的任何类型)


鉴于函数的用途,它应该返回布尔值,因此在第一种情况下返回
nil
没有意义。空列表编码一个没有元素的集合,因此您应该在那里返回
false

您似乎将集合表示为
(U,V)
,其中
U
是一个通用集合,
V
是一个布尔向量(两者都表示为相同长度的列表)。这当然是合理的,尽管@SimonShine在其出色的回答中指出,这并不是最有效的

使用您的表示,您可以使用模式匹配定义函数,让SML的类型推断推断出最通用的类型:

fun elementOf (x,([],[])) = false
|   elementOf (x,(y::ys,z::zs)) = if x = y then z else elementOf (x,(ys,zs));
推断类型为

''a * (''a list * bool list) -> bool
“a
指的是任何相等类型——因此您的函数可以用于表示任何类型的集合(只要该类型具有相等的概念),而不仅仅是整数。例如,
elementOf(“cat”、([“the”、“cat”、“in”、“hat”]、[true、true、false、true])
的计算结果为
true

在SML/NJ中,该定义给出了一个警告:
警告:匹配非穷尽性
。这是因为定义假定列表的长度相同。您可以保持代码不变——如果布尔列表的长度与通用集合的长度不同,则函数可能会崩溃,或者您可以确定
false
是一个合理的默认值,并将定义修改为:

fun elementOf (x,([],_)) = false
|   elementOf (x, (_,[])) = false
|   elementOf (x,(y::ys,z::zs)) = if x = y then z else elementOf (x,(ys,zs));

您可以将前两个案例合并,并将它们作为最后一个案例
elementOf(x,)=false
@SimonShine很好地观察到,尽管有一些关于使基本案例更明确的更明确的模式的说法。
fun elementOf (x,([],_)) = false
|   elementOf (x, (_,[])) = false
|   elementOf (x,(y::ys,z::zs)) = if x = y then z else elementOf (x,(ys,zs));