我正在做一些来自“真实世界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
的列表(任意大小)。我做错了什么?
答案 0 :(得分:14)
作为一种类型,[a]
代表“任意大小的a
s列表”。但是,作为一种模式,[a]
代表“一个只包含一个元素的列表,此后将以名称a
”知道。类似地,[a,b]
将表示“包含两个元素的列表,其中第一个应被称为a
,第二个应被称为b
”等等。正如您已经知道的那样,[]
代表“一个包含0个元素的列表”。
这类似于将列表文字作为表达式编写的方式。即如果你写myList = []
,myList
是空列表,如果你写myList = [x]
,myList
是一个只包含一个元素的列表,它是变量{{的值1}}。
答案 1 :(得分:4)
[]是空列表,一个不包含任何内容的列表。 [a]是一个只包含一个元素的列表,该元素(不是列表)将在函数中标识为“a”。 因此,您仍需要考虑列表包含多个元素的情况。
如果您只使用“a”而不是“[a]”,那么“a”将引用整个列表,您可以开始将它与您必须使用的功能分开。
请注意,您已经处理了空列表的情况,因此您不需要在if语句中再次检查它。
答案 2 :(得分:3)
在Haskell中你必须习惯的一件事,在你有经验之前不是很直观,是类型级事物的“命名空间”与值级别的名称空间完全分开的东西。这意味着当你谈论类型时,相同的源文本可能具有完全不同的含义,而不是在谈论价值时。
safeInit :: [a] -> Maybe [a]
::
之后的所有内容都在谈论类型。此处[a]
是应用于类型变量a
的列表类型构造函数。 1 因此它是列表的类型 (任何大小)其元素的类型为a
。
safeInit [a] = if length [a] <= 1
...
OTOH此等式位于值级别。这里[a]
不是类型,它是值(在=
的左侧,它是一个与之匹配的模式应用safeInit
的值;在右侧,它只是一个值)。在值级别,方括号语法不是列表类型构造函数,它是用于编写列表的语法糖,列表中的所有元素用括号内的逗号分隔。所以[a]
是包含一个单独元素的列表的值,由变量a
表示。如果我们希望列表为空,我们会写[]
。如果我们希望列表只包含a
和b
,我们会写[a, b]
等等。
在价值级别,[a]
成为任意数量a
的列表没有多大意义,因为(在此代码的任何特定评估期间)a
是一个特定值,例如3.4
。对于任意数量的3.4
s列表的表达式有什么用处?
1 正如Maybe a
是应用于类型变量Maybe
的{{1}}类型构造函数一样;列表类型构造函数唯一特别之处在于它被称为a
而不是普通名称,并且为应用程序而不是通常的前缀形式获取这种奇怪的“周围”语法。
答案 3 :(得分: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]
匹配一个只有一个元素的列表。