我一直在尝试计算整数3元组列表中的元素,它等于使用SML的给定整数,但它不起作用。任何人都可以帮我弄清楚下面的代码有什么问题或为我理顺吗?
fun number_in_month(x : int*int*int list, m: int) =
if null x then 0
else
let fun inc x = x + 1;
in
val counter = 0;
if m = #2 (hd x) andalso m > 0 then inc counter
number_in_month((tl x), m)
` else
number_in_month((tl x), m)
end
该函数应该返回m等于列表中每个元组的第二个元素的次数。
答案 0 :(得分:6)
显然,你很难放下你的命令性思维。
让我试着解决你的一些问题
您应该使用模式匹配,而不是使用null x
,hd x
和tl x
。
这也适用于分解元组和记录。例如
fun number_in_month ((x1, x2, x3) :: xs, m) = ...
或者,因为我们不使用x1和x3
fun number_in_month ((_, x2, _) :: xs, m) = ...
这样可以清楚地看到第一个参数是3元组的列表,没有类型注释 需要
此外,当您省略显式类型注释时,这是拥有类型系统的整个想法 可以为你推断它们(见下一点),然后是这段代码
fun foo42 xs = map (fn x => #2 x) xs
会在“未解析的弹性记录”上给你一些讨厌的错误(此错误消息来自SML / NJ)
/tmp/sml20620PlF:105.5-105.44 Error: unresolved flex record
(can't tell what fields there are besides #2)
可以通过分解3元组轻松修复
fun foo42 xs = map (fn (_, x2, _) => x2) xs
说到类型注释。它们(几乎总是)不需要它们,它们会混乱 代码的可读性。更不用说它们会不必要地限制您运行的类型 可用于。
根据你真正想要的,你给出的类型注释也是错误的。您
应该在int * int * int
附近放置括号。目前它被解释为
两个整数的3元组和一个整数列表int * int * (int list)
。
如果你真的坚持使用类型注释你的功能,那么你就可以这样做
val number_in_month : (int * int * int) list * int -> int =
fn ([] , m) => 0
| ((_,x2,_) :: xs, m) => 42
这几乎就像Haskell一样,在函数声明之前就给出了类型。
尝试在缩进代码时更加一致。这会让你更清晰。
在这里,我特别想到了缩进else
部分结束in ... end
的方式
部分。下面的部分显然仍然是错误的,在很多方面我无法想象,但它
给出了如何做到这一点的想法
fun number_in_month(x : int*int*int list, m: int) =
if null x then 0
else
let fun inc x = x + 1;
in
val counter = 0;
if m = #2 (hd x) andalso m > 0 then
inc counter
number_in_month((tl x), m)
else
number_in_month((tl x), m)
end
您不能在let-expression的val counter = 0
部分内声明变量in ... end
。
let-expression的语义是
let
dec
in
exp_1; ...; exp_n
end
因此所有声明(函数和值绑定等)都必须放在let ... in
部分。
地球上没有必要具有增量功能,它只会使可读性变得混乱。 请记住,SML使用单一赋值,因此变量在声明后是不可变的。
嵌套if-expression
中的序列事物inc counter
number_in_month((tl x), m)
绝对没有意义。你可以在里面拥有多个表达式的唯一方法
then ... else
部分(实际上是任何预期单个表达式的地方)都带有
sequence(exp_1; ...; exp_n)。但是,只有在除最后一个表达式之外的所有表达式之外,
副作用,因为他们的结果被忽略/扔掉
- (print "Foo\n"; print "Bar\n"; 42);
Foo
Bar
val it = 42 : int
如果你在这里搜索一下,你会发现最近一个非常相似的问题asked和answered。虽然它在最后一个参数的类型上有所不同,但你仍然可以得到一些有用的指针。
总而言之,解决方案可能看起来像
fun number_in_month ([], _) = 0
| number_in_month ((_,x2,_) :: xs, m) =
if x2 = m then
1 + number_in_month(xs, m)
else
number_in_month(xs, m)
但是,由于您的问题比前面提到的问题简单,您可以轻松地使用基础库中列表模块中的一些高阶函数
fun number_in_month (xs, m) = length (List.filter (fn (_, x2, _) => x2 = m) xs)
甚至(可以说)更简单,通过折叠列表并在每次匹配时递增变量
fun number_in_month (xs, m) = foldl (fn ((_, x2, _), b) => if x2 = m then b+1 else b) 0 xs
答案 1 :(得分:1)
fun number_in_month (L : (int*int*int) list, m : int) =
if L = nil
then 0
else
(if #2 (hd L) = m then 1 else 0) + number_in_month (tl L,m);
测试:
number_in_month ([] , 2);
number_in_month ([(1,2,3)] , 2);
number_in_month ([(1,2,3),(2,2,2)] , 2);
number_in_month ([(1,2,3),(2,2,2),(19,11,29)] , 2);
number_in_month ([(1,2,3),(2,2,2),(19,11,29),(10,28,19)] , 2);
number_in_month ([(1,2,3),(2,2,2),(19,11,29),(10,2,19)] , 2);
number_in_month ([(1,2,3),(2,2,2),(19,11,29),(10,28,19)] , 2);
number_in_month ([(1,2,3),(2,2,2),(19,11,29),(10,28,19)] , 2);
number_in_month ([(1,2,3),(2,2,2),(19,11,29),(10,28,19),(16,2,7)] , 2);
参考: