【问题标题】:What does this function signature mean in sml?这个函数签名在 sml 中是什么意思?
【发布时间】:2013-01-04 03:45:57
【问题描述】:

我正在查看我的教授给出的关于语言 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中不是只允许一个参数吗?

【问题讨论】:

  • cs.princeton.edu/courses/archive/fall08/cos441/notes/… 这是一种函数定义形式,你会在基于 ML 的语言、haskell、lisp 中一遍又一遍地看到:如果你点击了空列表,那么函数就完成了,否则进程头列表和递归
  • 你能解释一下函数lp接受什么参数以及curr是什么?参数是元组(curr,someList)吗?

标签: function sml smlnj


【解决方案1】:

这意味着lp 是一个带有两个参数的函数,第一个是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 函数中实际上只取一个值,但柯里化可以让您模拟多个参数。

让我们创建一个名为add 的函数,它将两个数字相加:

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 显然是用柯里化实现的。和类型 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。所以这里没有咖喱。

使用哪个是品味、惯例和实际考虑的问题。

希望这会有所帮助。

【讨论】:

  • 如果不是太多工作,你能告诉我lp 如果被实现为lp (curr, someList) 会是什么样子吗?谢谢
  • 在机器学习中?刚加了。希望对您有所帮助。
  • @Faiz "事实上可以肯定地说,在某种程度上,前者是后者的语法糖"。它确实是一种派生形式,尽管您提供的形式并未一直扩展为等效形式。
  • 啊,你是说val add = fn a => fn b => a + b?是的,为了完整起见,我应该添加它!谢谢!
【解决方案2】:

只是为了澄清一下柯里化,来自 Faiz 的出色回答。

如前所述,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)

为了完整起见,cases其实也是一种派生形式,以匿名函数为等价形式

val rec pow = fn n => fn k => 
    (fn (n,0) => 1
      | (n,k) => n * pow n (k-1)) (n,k)

请注意,(n,k) 直接应用于最里面的匿名函数。

【讨论】:

    猜你喜欢
    • 2018-02-06
    • 2011-01-29
    • 2021-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多