2013-07-31 46 views
3

我正在从“真实世界哈斯克尔”做一些练习。一个是设计一个安全版本init :: [a] -> [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的(任意大小)的列表下。我究竟做错了什么?

+0

作为模式匹配的'[a]'是字面上的一个元素列表。我想你可能是'xs'或'(x:xs)'的意思。尝试用'xs'或其他一些以小写字母开头的变量替换函数定义中的[[]](不是类型)。 –

回答

14

作为一种类型,[a]的确代表“任何尺寸的a s”的列表。然而,作为一种模式,[a]代表“仅包含一个元素的列表,其将从此名称a”。同样,[a,b]意味着“一个包含两个元素的列表,其中第一个元素将被称为a,第二个元素将被称为b”等等。正如你已经知道的那样,[]代表“包含正好0个元素的列表”。

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

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]与仅包含一个元素的列表匹配。

+5

请注意,像这样使用'length'不是惯用的Haskell,对于大型列表来说效率也不高。使用模式匹配来排除长度为1的列表将是更好的解决方案。 – sepp2k

+2

另外'init [foo]'是'[]',不是一个例外,所以排除一个元素列表甚至不是一个名为'safeInit'的函数应该做的。 – sepp2k

4

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

如果您只是使用“a”而不是“[a]”,那么“a”将引用整个列表,您可以开始将它与您必须处理的功能区分开来。

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

3

在Haskell中你必须习惯的一件事是,直到你有相当的经验,它不是很直观,因为类型级事物的“名称空间”与值名称空间完全分开,水平的事情。这意味着当您谈论类型时,相同的源文本可能具有完全不同的含义,而不是在讨论值时。

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

::的一切都在谈论类型。这里[a]是列表型构造函数适用于类型变量a。 所以它的类型列表(任何大小)的元素是a类型。

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

OTOH这个方程是在水平。这里[a]不是,这是一个(在=的左手侧是到safeInit被施加到由值相匹配的图案;在右手侧,它只是一个值)。在值级别上,方括号语法不是列表类型的构造函数,它是写列表的语法糖,列表中的所有元素都用括号内的逗号分隔。所以[a]是包含一个单一元素的列表的值,其由变量a表示。如果我们希望清单是空的,我们会写[]。如果我们想在列表中只包含ab我们会写[a, b]

在价值层面,将没有多大意义的[a]任意数的a s的名单,因为(在对此代码的任何特定评估)a是一个特定值,例如3.4。对于任何数量的3.4 s的列表有什么用?


正如Maybe a是施加到所述类型变量aMaybe类型构造;关于列表类型构造函数唯一特别的地方是它被称为[]而不是普通名称,并且为应用程序获取这种奇怪的“环绕”语法,而不是通常的前缀形式。