SML错误:当将列表中的整数与整数进行比较时,运算符和操作数不一致

dtg*_*dtg 1 sml typechecking

我是标准ML的新手,无法弄清楚为什么我会遇到此类型不匹配错误:

fun number_in_month (month : int, dates : int list) =                                            
    if null dates                                                                                
    then 0                                                                                       
    else if (month = (hd (tl (hd dates))))            
    then number_in_month(month, (tl dates)) + 1
    else number_in_month(month, (tl dates))
Run Code Online (Sandbox Code Playgroud)

评估此函数会导致以下错误:

Error: operator and operand don't agree [tycon mismatch]     
 5  operator domain: 'Z list                                                                       
 6  operand:         int                                                                           
 7  in expression:                                                                                 
 8    tl (hd dates)     
Run Code Online (Sandbox Code Playgroud)

但是,在REPL中,如果我执行以下操作:

val x = [[84, 12, 23], [83, 01, 18]]
12 = (hd (tl (hd x)))                    (*  -> val it = true : bool *)
Run Code Online (Sandbox Code Playgroud)

我不确定在这种情况下类型检查规则是什么,我不明白为什么相同的表达式可以在REPL上起作用,但在我尝试评估函数中的子表达式时却不行.

Fai*_*aiz 7

你正在获得列表头部的头部.您的x(在REPL中)是一个int list list(整数列表).但是你的函数定义将它声明为int list.再宣布number_in_monthdates: int list list应解决您的问题:

fun number_in_month (month : int, dates : int list list) =  
   ...
Run Code Online (Sandbox Code Playgroud)

它在REPL中按预期工作,因为您定义时x没有明确声明它的类型.SML推断x的类型int list list(hd (tl (hd x))) 通过类型检查器的原因.

UPDATE

(当stackoverflow发生故障时尝试添加此权限)

如果你有兴趣,这里有一些关于如何重新编写代码以使其更具ML-ish的想法:

首先,您可以使用模式匹配:

fun number_in_month (month: int, []) = 0
  | number_in_month (month: int, ([y,m,d]::rest)) = 
      if month = m then number_in_month(month, rest) + 1
      else number_in_month(month, rest)
Run Code Online (Sandbox Code Playgroud)

所以number_in_month需要一个月的元组和一个日期列表,这在逻辑上是[]或者([y,m,d]::rest).这与您选择表示日期的方式(作为整数列表)兼容,但这将编译并match nonexhaustive发出警告.这是有道理的,因为如果你在传递时会发生什么dates作为[[84], [83]]?模式匹配方法至少会警告你这一点,但是使用代码就像(hd (tl (hd dates)))你的程序已成功检查类型一样,你会遇到运行时错误.您可以为日期列表中的日期少于/多于3个元素添加另一个模式匹配,但如果可能,将日期表示为3个整数的元组可能更清晰.

 type date = (int * int * int)
Run Code Online (Sandbox Code Playgroud)

然后你可以:

fun number_in_month (month: int, []: date list) = 0
  | number_in_month (month: int, ((y,m,d)::rest)) = 
      if month = m then number_in_month(month, rest) + 1
      else number_in_month(month, rest)
Run Code Online (Sandbox Code Playgroud)

此外,如果您更愿意重用代码,您可以尝试更高阶的函数(例如foldr):

fun number_in_month (month: int, dates: date list) =
  foldl (fn ((_,m,_), c) => if m = month then c+1 else c) 0 dates
Run Code Online (Sandbox Code Playgroud)

要么

fun number_in_month (month: int, dates: date list) =
  length (List.filter (fn (_,m,_) => m = month) dates)
Run Code Online (Sandbox Code Playgroud)

超过你的要求,但我希望它有所帮助.