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

问题描述 投票:1回答:1

我是Standard 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上运行,但是当我尝试评估函数中的子表达式时却不行。

sml typechecking
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)))的原因通过类型检查器。

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)

因此number_in_month用一个月的元组和一个日期列表,从逻辑上讲,它们是[]([y,m,d]::rest)。这与您选择的表示日期的方式兼容(作为一个int列表),但这将使用match nonexhaustive警告进行编译。这是有道理的,因为如果将dates传递为[[84], [83]]会发生什么?模式匹配方法至少会向您发出警告,但是使用(hd (tl (hd dates)))这样的代码,您将获得尽管您的程序已成功进行类型检查,但仍存在运行时错误。您可以添加另一个日期少于或多于3个元素的日期列表的模式匹配,但是如果可能,将日期表示为3个整数的tuples可能更干净。

 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)

超出您的要求,但希望对您有所帮助。

© www.soinside.com 2019 - 2024. All rights reserved.