ML类型的功能,一个小窍门?

时间:2016-02-12 09:39:43

标签: functional-programming ml

我准备GATE考试。最古老的问题之一是我们不熟悉。一些专家请为我们澄清一下。

  

以下哪一项可以作为以下ML功能的类型?

my f(g, nil)=nil | f(g,x::xs)=(fn a  ⇒ g(a,x))::f(g, xs);

1) (int *book → real) * bool list → (int → real) list
2) (bool *int → int) * real list → (bool → int) list
3) (int *int → real) * real list → (real → bool) list
4) (bool *real → int) * int list → (int → int) list

答案表说(1)是纠正。评论或简短描述,以便更好地理解?

1 个答案:

答案 0 :(得分:5)

您应该做的第一件事就是自己重写函数定义。这将迫使你实际解析和理解它。

fun f (g, nil)     = nil
  | f (g, x :: xs) = (fn a => g (a, x)) :: f (g, xs);

所以,f是一个函数,即使问题是这样,它必须有... -> ...类型:

val f : ... -> ...

收到什么?让我们来看看函数的第一个模式:

fun f (g, nil)     = nil

它是(a, b)形式的东西,它是一个2元组。函数的参数必须是元组:

val f : (... * ...) -> ...

仅仅通过查看定义,我们无法确定g必须具有哪种类型,但它使用nil作为元组的第二个元素,nil是空列表。这意味着元组的第二个组件必须是一个列表:

val f : (... * ... list) -> ...

我们还能推断出什么?返回值也是nil,这意味着该函数返回一个事物列表,不清楚某事是什么。

val f : (... * ... list) -> ... list

好的,现在让我们跳转到函数定义的第二个模式:

| f (g, x :: xs) = (fn a => g (a, x)) :: f (g, xs);

我们没有找到关于参数类型的更多信息,我们只是确认元组的第二个元素必须确实是一个列表,因为它使用了::构造函数。

让我们来看看身体:

(fn a => g (a, x)) :: f (g, xs)

看起来它正在构建一个列表,因此它必须返回一个列表,该列表与我们到目前为止所绘制的返回类型一致,即... list。让' S 试着找出元素的类型。

似乎通过递归调用我们当前正在调查的函数f来添加一个函数对象作为列表的头部。因此,我们返回的列表元素必须是函数:

val f : (... * ... list) -> (... -> ...) list

但是这个功能做了什么?它使用2元组参数调用g。现在我们可以填写有关2元组f收到的第一个元素的一些信息。它必须是一个接收2元组的函数:

val f : (((... * ...) -> ...) * ... list) -> (... -> ...) list

我们能否说明添加到返回列表的函数文字所收到的a参数?不是真的,只是它被传递给g。我们可以告诉x的类型吗?不是真的,只是它被传递给g。此外,ax之间是否有任何约束?看起来不像。到目前为止,我们只能说g的类型必须是这样的:

('a * 'b) -> 'c'

'a'b'c是多态类型,即任何具体类型都可以满足它们。你可以将它们视为整体。我们现在可以为f函数填写更多类型:

val f : ((('a * 'b) -> 'c) * ... list) -> (... -> ...) list

我们实际上可以做得更多,我们已经为x变量分配了一个类型,但这来自f收到的2元组的第二个参数,所以让&# 39; s也填写:

val f : ((('a * 'b) -> 'c) * 'b list) -> (... -> ...) list

我们甚至可以填写返回列表的元素类型,因为我们已经为此分配了类型。

val f : ((('a * 'b) -> 'c) * 'b list) -> ('a -> 'c) list

由于类型运算符优先级规则,我们可以从我们提出的类型中删除一些额外的括号而不改变含义:

val f : ('a * 'b -> 'c) * 'b list -> ('a -> 'c) list

现在,我们的功能类型已经完成。但是,此类型无法在可能的答案列表中找到,因此我们必须查看是否可以使用其中任何一种而不是我们已确定的内容。为什么?因为我们的函数类型使用类型变量,可以通过具体类型填充。让我们逐一介绍它们:

选择1

val f : ('a  * 'b   -> 'c)   * 'b list   -> ('a  -> 'c) list
val f : (int * bool -> real) * bool list -> (int -> real) list

看起来'a可能是int'b可能是boolbook就是你所拥有的'c粘贴,但我认为这是一个错字),val f : ('a * 'b -> 'c) * 'b list -> ('a -> 'c) list val f : (bool * int -> int) * real list -> (bool -> int) list 可能是真实的。所有替换都匹配这些对应关系,因此我们声明,是的,第一个选择可以是给定函数的可能类型,即使不是最通用的。让我们选择第二个选择:

选择2

'a

类型变量到具体类型的对应表可以是:

  • bool - > 'b
  • int - > 'c
  • int - > 'b
  • real - > 'b

我们可以在此处停止,因为我们可以看到{{1}}已分配给不同类型,因此无法将此功能签名分配给我们已实施的实施。

选择3和4

它们与选择2类似,但我会将它们作为练习给读者:)