空函数的实现

Implementation of null function

我必须为大学学习 Haskell,因此我开始使用 learnyouahaskell.com。
我一直使用命令式语言,所以我决定练习 Haskell 比我用其他语言编写的代码多得多。
我开始实现几个函数来处理列表,例如 headtailinit、...
在某些时候,我查找了这些函数的实现以与我的进行比较,我偶然发现了 List.lhs.

中定义的空函数

null 的实现:

-- | Test whether a list is empty.
null                    :: [a] -> Bool
null []                 =  True
null (_:_)              =  False

我的实现:

mNull :: [a] -> Bool
mNull []        = True
mNull _         = False

我知道即使是这么简单的问题也没有愚蠢的问题:)
所以我的问题是为什么最初的实现使用 (_:_) 而不是 _?
使用 (_:_) 是否有任何优势,或者是否有任何我不知道的边缘情况?
我真的无法想象任何优势,因为 _ 抓住了一切。

因为_字面上的意思是除了明确指定的模式之外的任何东西。当您指定 (_:_) 时,它表示可以表示为包含至少 1 个元素的列表的任何内容,而无需理会列表实际包含的元素或什至有多少元素。由于已经存在具有空列表显式模式的情况,因此 (_:_) 也可以替换为 _.

但是,将其表示为 (_:_) 使您可以灵活地甚至不显式传递空列表模式。事实上,这会起作用:

-- | Test whether a list is empty.
null                    :: [a] -> Bool    
null (_:_)              =  False
null _                  =  True

Demo

你不能只用你的解决方案来改变条款:

mNull' :: [a] -> Bool
mNull' _  = False
mNull' [] = True

这个 将 总是 产生 False,即使你传递一个空列表。因为运行时从不考虑 [] 子句,它会立即看到 _ 匹配任何内容。 (GHC 会警告您这种重叠模式。)

另一方面,

null' :: [a] -> Bool
null' (_:_) = False
null' []    = True

仍然可以正常工作,因为 (_:_) 无法匹配空列表的特殊情况。

这本身并没有真正给两个显式子句带来优势。然而,在更复杂的代码中,写出所有互斥选项有一个好处:如果您忘记了一个选项,编译器也会警告您!而 _ 可以并且只会处理前面条款未处理的任何情况,即使那实际上并不正确。

我将补充 leftaroundabout 所说的内容。这并不是列表类型的真正潜在问题,但通常,数据类型有时会被修改。如果你构思不当

data FavoriteFood = Pizza
                  | SesameSpinachPancake
                  | ChanaMasala

之后你学会喜欢 DrunkenNoodles 并将其添加到列表中,你所有在类型上进行模式匹配的函数都需要扩展。如果您使用过任何 _ 模式,您将需要手动找到它们;编译器帮不了你。如果你已经明确地匹配了每一件事,你可以打开 -fwarn-incomplete-patterns,GHC 会告诉你你错过的每个地方。