在标准 ML 中,以下声明有什么区别(我省略了以 开头的定义=
):
fun f x y;
和
fun f (x, y);
据我了解,第一个需要两个参数,而第二个需要一个元组。如果是这样,那么使用一个与另一个的(实际)含义是什么?可以争论哪种风格是最好的吗?假设,实际上不需要将元组作为一个整体使用,即只有x
和y
分别是相关的。
在标准 ML 中,以下声明有什么区别(我省略了以 开头的定义=
):
fun f x y;
和
fun f (x, y);
据我了解,第一个需要两个参数,而第二个需要一个元组。如果是这样,那么使用一个与另一个的(实际)含义是什么?可以争论哪种风格是最好的吗?假设,实际上不需要将元组作为一个整体使用,即只有x
和y
分别是相关的。
是的,正如您所说,第一个采用“两个参数”,而第二个采用一个元组参数。但是,要了解真正发生的事情,您必须了解currying。在 ML 中,每个“函数”都只接受一个参数(不多也不少)。
在第二种情况下,这很容易理解,它接受一个参数,它是一个元组,它对元组中的东西做一些事情,并返回一个结果。在第一种情况下,您定义了一个接受一个参数的函数,然后返回一个接受另一个参数的函数,然后返回结果。
比如说,这是一个接受两个int
s 并返回一个 s的函数int
。查看这两个函数的类型是有启发性的:第二个函数的类型是(int * int) -> int
,即从元组到 int 的函数。第一个函数的类型是int -> int -> int
,因为->
它是右结合的,所以可以解析为int -> (int -> int)
。所以你可以看到,它接受一个 int 并返回一个函数。语法fun f x y = ...
是更详细的语法糖val f = fn x => fn y => ...
。当您应用此函数时,例如f 3 4
,函数应用程序是左关联的,因此实际上是(f 3) 4
,因此您可以看到它是如何工作的:f
接受一个int
,返回一个然后应用于另一个 int 的函数。这就是柯里化的要点。ML 的语法只是让它变得透明。
curried 版本(第一个版本)允许您执行部分应用程序。这意味着,尽管您的函数在概念上需要两个参数,但您不必给它那么多参数。所以f 3 4
我们上面的 (即(f 3) 4
),如果我们只取f 3
,而不是将其应用于4
,只需将其保留并将其存储在变量或其他东西中怎么办?通过“给函数提供比它想要的更少的参数”,我们自动得到一个接受剩余参数的函数。例如,我们可以这样做map (f 3) someList
,它会为列表中f 3 x
的每个计算一个列表x
,而无需我们编写一些复杂的语法。
在标准 ML 中,库函数大多采用非柯里化形式;而在 OCaml 和 Haskell 中,它们主要是咖喱形式。(请参阅这个问题。)但是,作为高阶函数(如 map 和 fold)的 SML 库函数倾向于将它们的函数参数放在单独的(curried)参数中。