1

我是标准 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))

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

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

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

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

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

4

1 回答 1

7

您正在获得列表头部的尾部头部。您的x(在 REPL 中)是一个int list list(整数列表的列表)。但是您的函数定义将其声明为int list. 重新声明应该number_in_month可以dates: int list list解决您的问题:

fun number_in_month (month : int, dates : int list list) =  
   ...

它可以像您在 REPL 中所期望的那样工作,因为您在x没有明确声明它的类型的情况下进行定义。SML 推断 x 的类型int list list(hd (tl (hd x))) 通过类型检查器的原因。

更新

(试图在stackoverflow关闭时添加这个权利)

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

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

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)

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

 type date = (int * int * int)

然后你可以有:

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)

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

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

或者

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

比你要求的要多,但我希望它有所帮助。

于 2013-01-21T04:43:12.780 回答