3

我正在做一些来自“Real World Haskell”的练习。一是设计一个安全版本的init :: [a] -> [a].

我应该从safeInit :: [a] -> Maybe [a]

这就是我目前所拥有的。

safeInit :: [a] -> Maybe [a]
safeInit [] = Nothing
safeInit [a] = if length [a] <= 1
    then Nothing
    else Just (take (length [a] -1) [a])

在 GCHi 中,测试时safeInit [1,2]我收到错误消息

*例外:ch4exercise.hs:(21,1)-(24,44):函数 safeInit 中的非详尽模式

我的印象[a]只是代表a's 的列表(任何大小)。我究竟做错了什么?

4

4 回答 4

14

作为一种类型,[a]确实代表“任何大小的as 的列表”。然而,作为一种模式,[a]它代表“一个仅包含一个元素的列表,此后将以其名称为人所知a”。类似[a,b]的意思是“一个包含两个元素的列表,第一个元素被称为a,第二个元素被称为b”等等。[],正如您似乎已经知道的那样,代表“正好包含 0 个元素的列表”。

这类似于您将列表文字编写为表达式的方式。即,如果你写myList = []myList是空列表,如果你写myList = [x]myList是一个包含一个元素的列表,它是变量的值x

于 2013-07-31T16:04:04.313 回答
4

[] 是空列表,一个不包含任何内容的列表。[a] 是一个仅包含一个元素的列表,并且该元素(不是列表)在函数中将被标识为“a”。因此,您仍然需要考虑列表包含多个元素的情况。

如果您只使用“a”而不是“[a]”,那么“a”将指代整个列表,您可以开始将其与您手头的功能分开。

请注意,您已经处理过列表为空的情况,因此您不需要在 if 语句中再次检查它。

于 2013-07-31T16:05:13.753 回答
3

在 Haskell 中你必须习惯的一件事,在你有相当的经验之前不是很直观,那就是类型级事物的“命名空间”与值级事物的命名空间完全分开。这意味着当您谈论类型时,与谈论值时相同的源文本可能具有完全不同的含义。

safeInit :: [a] -> Maybe [a]

后面的所有内容都在::谈论类型。这是应用于类型变量[a]的列表类型构造函数a1所以它是列表的类型(任何大小),其元素是 type a

safeInit [a] = if length [a] <= 1
    ...

OTOH这个等式处于价值水平。这里[a]不是type,它是一个(在它的左侧,=它是一个与应用的值匹配的模式safeInit;在右侧,它只是一个值)。在值级别,方括号语法不是列表类型构造函数,它是编写列表的语法糖,列表的所有元素在括号内用逗号分隔。[a]包含一个元素的列表的值也是如此,它由变量 表示a。如果我们希望列表为空,我们会写[]. 如果我们希望列表包含 justa并且b我们会写[a, b]等。

[a]在值级别上,作为任意数量 s 的列表没有多大意义a,因为(在此代码的任何特定评估期间)a是一个特定值,例如3.4. 3.4对于任意数量的s的列表,表达式有什么用?


1就像Maybe aMaybe类型构造函数应用于类型变量一样a;列表类型构造函数的唯一特殊之处在于它被调用[]而不是普通名称,并且为应用程序获得了这种奇怪的“环绕”语法,而不是通常的前缀形式。

于 2013-08-01T00:18:35.580 回答
0

这条线有问题:

safeInit [a] = if length [a] <= 1

在等式的左侧,[a]将匹配一个只有一个元素的列表。所以编译器看到你有一个safeInit空列表的版本,safeInit一个有一个元素的列表的版本,但没有一个有更多元素的列表。这就是它抱怨的原因Non-exhaustive patterns

我认为你真正想要的是

safeInit a = if length a <= 1
    then Nothing
    else Just (take (length a -1) a)

总结一下:

  • 在类型签名中,[a]代表任意类型的元素列表a

  • 在一个模式中,[a]匹配一个只有一个元素的列表。

于 2013-07-31T16:05:00.347 回答