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