我正在查看我的教授给出的关于语言SML的一些注释,其中一个函数看起来像这样:
fun max gt =
let fun lp curr [] = curr
| lp curr (a::l) = if gt(a,curr)
then lp a l
else lp curr l
in
lp
end
有人可以帮忙解释一下这是做什么的吗?我最困惑的是这条线:
let fun lp curr [] = curr
这究竟是什么意思?据我所知,有一个名为lp
的函数,但curr []
是什么意思?这些论点?如果是这样,你不是只允许在sml中使用一个参数吗?
答案 0 :(得分:7)
这意味着lp
是一个带有2个参数的函数,第一个是curr
,第二个是一个列表,逻辑上可能是空的[]
)或包含至少一个元素((a::l)
是列表的模式,其中a
位于头部,列表的其余部分为l
)
如果要将那段FP代码翻译成某种众所周知的命令式语言,它看起来像是:
function lp(curr, lst) {
if (lst.length == 0) {
return curr;
} else {
var a = lst[0]; // first element
var l = lst.slice(1, lst.length); // the rest
if (gt(a, curr)) {
return lp(a, l);
} else {
return lp(curr, l)
}
}
}
非常满口,但这是一个忠实的翻译。
函数式语言基于Lambda Calculus,其中函数只取一个值并返回一个结果。虽然SML和其他FP语言都是基于这种理论,但它在实践中相当不方便,因此许多这些语言允许您通过所谓的Currying表达将多个参数传递给函数。
所以是的,在ML函数中实际上只接受一个值,但currying让你模拟多个参数。
让我们创建一个名为add
的函数,它会添加2个数字:
fun add a b = a + b
应该这样做,但我们定义了2个参数。 add
的类型是什么?如果您查看REPL,它是val add = fn : int -> int -> int
。哪个读取,“add是一个函数,它接受一个int并返回另一个函数(它接受一个int并返回一个int)”
所以我们也可以用这种方式定义add
:
fun add a =
fn b => a + b
你会发现它们是相似的。事实上,在某种程度上可以说是安全的 前者是后者的语法糖。 因此,您在ML中定义的所有函数,即使是具有多个参数的函数,实际上都是具有一个参数的函数,它返回接受第二个参数的函数,依此类推。起初有点难以适应,但它 很快成为第二天性。
fun add a b = a + b (* add is of type int -> int -> int *)
add 1 2 (* returns 3 as you expect *)
(* calling add with only one parameter *)
val add1 = add 1
什么是add1
?这是一个函数,它会将1
添加到您传递的单个参数中!
add1 2 (* returns 3 *)
这是部分应用程序的一个示例,您在其中调用函数零碎, 一次一个参数,每次都返回,另一个接受其余的函数 论证。
此外,还有另一种赋予多个参数外观的方法:元组:
(1, 2); (* evaluates to a tuple of (int,int) *)
fun add (a,b) = a + b;
add (1, 2) (* passing a SINGLE argument to a function that
expects only a single argument, a tuple of 2 numbers *)
在您的问题中,lp
也可以将实施为lp (curr, someList)
:
fun max gt curr lst =
let fun lp (curr, []) = curr
| lp (curr, (a::l)) = if gt(a,curr) then lp (a, l)
else lp (curr, l)
in
lp (curr, lst)
end
请注意,在这种情况下,我们必须将max
声明为max gt curr lst
!
在您发布的代码中,lp
显然是通过currying实现的。和类型
max
本身就是fn: ('a * 'a -> bool) -> 'a -> 'a list -> 'a
。把它分开:
('a * 'a -> bool) -> (* passed to 'max' as 'gt' *)
'a -> (* passed to 'lp' as 'curr' *)
'a list -> (* passed to 'lp' as 'someList' *)
'a (* what 'lp' returns (same as what 'max' itself returns) *)
请注意gt
的类型,max
的第一个参数:fn : (('a * 'a) -> bool)
- 它是一个参数的函数('a * 'a)
,一个由两个'a
组成的元组,它返回一个'a
。所以不要在这里讨好。
使用哪个是品味,惯例和实际考虑因素。
希望这有帮助。
答案 1 :(得分:4)
从Faiz的优秀答案来澄清一点关于currying的事。
如前所述,SML只允许函数采用1个参数。这是因为函数fun foo x = x
实际上是(语法糖)val rec foo = fn x => x
的派生形式。实际上这并不完全正确,但让我们保持简单的第二个
现在以此功能为例。这里我们将函数声明为“取两个参数”
fun pow n 0 = 1
| pow n k = n * pow n (k-1)
如上所述,fun ...
是派生形式,因此幂函数的等价形式是
val rec pow = fn n => fn k => ...
正如您在此处所看到的,我们在表达原始函数声明的两个不同模式匹配时遇到问题,因此我们不能再保持“简单”和fun
声明的真正等效形式是
val rec pow = fn n => fn k =>
case (n, k) of
(n, 0) => 1
| (n, k) => n * pow n (k-1)
为了完整起见,案例实际上也是派生形式,匿名函数作为等价形式
val rec pow = fn n => fn k =>
(fn (n,0) => 1
| (n,k) => n * pow n (k-1)) (n,k)
请注意,(n,k)
直接应用于最内在的匿名函数。