我是标准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上起作用,但在我尝试评估函数中的子表达式时却不行。
答案 0 :(得分:6)
你正在获得列表头部的头部。你的x
(在REPL中)是
a 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-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)
。这与您选择表示日期的方式兼容
(作为整数列表),但这将编译match nonexhaustive
警告。这是有道理的,因为如果你将dates
传递给[[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)
超过你的要求,但我希望它有所帮助。