我必须学习Haskell大学,因此我开始使用learnyouahaskell.com。我总是使用命令式语言,因此我决定通过编码比我更多地编写Haskell语言。
我开始实施多个功能来处理head
,tail
,init
,...等列表
在某些时候,我查找了这些函数的实现,以便与我进行比较,我偶然发现了List.lhs
中定义的null函数。
null的实现:
-- | Test whether a list is empty.
null :: [a] -> Bool
null [] = True
null (_:_) = False
我的实施:
mNull :: [a] -> Bool
mNull [] = True
mNull _ = False
我知道即使是这么简单的问题也没有愚蠢的问题:)
所以我的问题是为什么原始实现使用(_:_)
而不仅仅是_
?
使用(_:_)
是否有任何优势,或者是否有任何我不知道的边缘情况?
我无法想象任何优势,因为_
能抓住一切。
答案 0 :(得分:39)
你不能只用你的解决方案来改变条款:
mNull' :: [a] -> Bool
mNull' _ = False
mNull' [] = True
此
即使您传递一个空列表,总是会产生False
。因为运行时没有考虑[]
子句,所以它立即看到_
匹配任何内容。 (GHC会警告你这种重叠的模式。)
另一方面,
null' :: [a] -> Bool
null' (_:_) = False
null' [] = True
仍然可以正常工作,因为(_:_)
无法匹配空列表的特定情况。
这本身并没有真正赋予这两个明确的条款优势。但是,在更复杂的代码中,写出所有相互删除的选项有一个好处:如果您忘记了一个选项,编译器也可以向您发出警告!鉴于_
可以而且只会处理前一条款未处理的任何案件,即使这些案件实际上并不正确。
答案 1 :(得分:6)
因为_
字面意思是除明确指定的模式之外的任何东西。当您指定(_:_)
时,它表示可以表示为包含至少1个元素的列表的任何内容,而不会打扰列表实际包含的内容甚至多少元素。由于已存在具有空列表的显式模式的案例,(_:_)
也可能被_
替换。
但是,将其表示为(_:_)
可以让您灵活地甚至不显式传递空列表模式。事实上,这将有效:
-- | Test whether a list is empty.
null :: [a] -> Bool
null (_:_) = False
null _ = True
答案 2 :(得分:4)
我将添加左下方所说的内容。这实际上不是列表类型的潜在问题,但通常,数据类型有时会被修改。如果你有一个错误的想法
data FavoriteFood = Pizza
| SesameSpinachPancake
| ChanaMasala
以后您学会喜欢DrunkenNoodles
并将其添加到列表中,您需要扩展所有类型匹配的函数。如果您使用过任何_
模式,则需要手动查找;编译器无法帮助你。如果您明确地匹配每件事,您可以打开-fwarn-incomplete-patterns
,GHC会告诉您有关您错过的每个地点。