1

这是我的功能:

fun inclist [] 0 = []
  | inclist (l, inc) = map (fn x => x + inc) l;

l是一个整数列表,我试图添加incl.

但我收到了这些错误

Function: inclist : int list * int -> int list
Argument: [1, 2, 3, 4, 5] : int list
Reason: Can't unify int list to int list * int (Incompatible types)

Function: inclist [1, 2, 3, 4, ...] : int list
Argument: 1 : int
Reason: Value being applied does not have a function type

而且我不明白为什么,因为我有一个几乎相同的函数,可以将值相乘,效果很好。

4

4 回答 4

2

为了澄清较早的答案,鉴于 OP 的代码表明了 SML 知识的新手水平:

每个 SML 函数都只接受一个参数。现在,这听起来很疯狂,因为我们都见过需要多个参数的函数。有两种方法可以调和这两件事。

咖喱

函数是 SML 中的第一类值。我们可以很容易地编写一个函数而不给它一个名字。

fn x => x + 1

我们可以将它绑定到一个名称。

val add1 = fn x => x + 1

这些函数有类型int -> int。此类型签名表明该函数接受一个 int 并返回一个 int。

有一个方便的语法。

fun add1 x = x + 1

如果我们想要一个函数接受多个参数......我们不能。但是我们接受一个 int 的函数可以返回一个接受一个 int 并返回一个 int 的函数。这种函数的类型签名是int -> int -> int.

val add = fn x => fn y => x + y

再一次,有一个更方便的语法来写这个。

fun add x y = x + y

调用它很简单:

add 42 27

但我们可以部分应用该功能。例如,提供1add取回一个添加1到发送给它的任何 int 的函数。

val add1 = add 1

元组

SML 的表亲 OCaml 几乎完全使用柯里化。在 SML 中,使用元组为函数提供多个参数通常更为惯用。

元组是单个值,但它包含多个可能具有异构类型的值。考虑两个整数的简单元组:(3, 4). 语法上,元组被括号包围,值用逗号分隔。

像我们之前的add函数一样添加两个整数但使用元组的函数如下所示:

fun add (x, y) = x + y

在许多情况下,函数名和元组之间的空格被省略了。对于那些熟悉类 C 语法的人来说,这可能会误导实际发生的事情。

fun add(x, y) = x + y

你的代码

因此,查看您编写的代码:

fun inclist [] 0 = []
  | inclist (l, inc) = map (fn x => x + inc) l;

第一行定义了类型的柯里化函数,'a list -> int -> 'a list第二行定义了类型的函数int list * int -> int list

由于这两个签名不匹配,您的代码无法编译。

还有一件事

在您的代码中,您不必要地专门化了模式匹配。

fun inclist [] 0 = []

您不需要将第二个参数匹配为0. 以任意数量递增一个空列表将始终是一个空列表。

于 2021-09-22T06:21:23.060 回答
2

那不是您的实际代码——该代码不会产生这些消息。
您的实际代码看起来更像这样:

fun inclist ([], 0) = []
  | inclist (l, inc) = map (fn x => x + inc) l;

inclist [1,2,3,4,5] 1;

具有相同形状的定义子句。
您已经定义了一个函数,它接受一anint list和 anint并返回一个int list
正如错误消息所说:int list * int -> int list

然后你尝试将此函数应用于不是这样一对的东西。
inclist [1,2,3,4,5] 1(inclist [1,2,3,4,5]) 1- 它首先应用于inclist[1,2,3,4,5]然后将结果 - 必须是函数 - 应用于1

您的第一条消息是指inclist [1,2,3,4,5],并说这[1,2,3,4,5]不是int list * intinclist需要。

第二条消息是指您申请inclist [1,2,3,4,5], 1wheninclist [1,2,3,4,5]不是函数。
(根据前面的消息,当它有类型错误时,polyml 声称它inclist [1,2,3,4,5]是一个有点奇怪int list。一种可能的解释是,polyml 不会费心检查参数的类型,而是“inclist应用于任何参数是int list“。)

解决方案是像函数期望的那样传递一对:

inclist ([1,2,3,4,5], 1)

或以“咖喱”方式定义函数:

fun inclist [] 0 = []
  | inclist l inc = map (fn x => x + inc) l;

inclist [1,2,3,4,5] 1;

另一方面,第一个特殊情况是不必要的。
如果您担心效率低下,那么使用特殊情况0[](避免在巨大列表中添加零可能是有益的;仅使用空列表避免它是没有意义的)更有意义。

fun inclist (l, 0) = l
  | inclist (l, inc) = map (fn x => x + inc) l;

但这也可以完成这项工作:

fun inclist (l, inc) = map (fn x => x + inc) l;
于 2021-09-22T12:08:04.567 回答
1

您的第一个模式以 curried 方式定义,而您的第二个模式使用元组方式。

这应该有效:

fun inclist [] _ = []
  | inclist l inc = map (fn x => x + inc) l;

请注意,我稍微更改了模式,因为当列表为空时,我们真的不在乎增量是多少。

事实是,这可能map已经做了,所以很可能你可以取消第一个模式,只保留你的映射函数模式。

但无论如何,现在你可以这样做:

inclist [] 100; 
inclist [1,2,3] 5;

或者,您可以使用括号定义这两种功能模式。

于 2021-09-22T05:32:10.177 回答
0

即使这样也可以...

fun inclist inc = map (fn x => x + inc);

可能不是现在,但在探索地盘的某个时间点Higher-Order Functions,这件事会很容易理解。

于 2021-09-22T06:36:42.853 回答