空函数的实现
Implementation of null function
我必须为大学学习 Haskell,因此我开始使用 learnyouahaskell.com。
我一直使用命令式语言,所以我决定练习 Haskell 比我用其他语言编写的代码多得多。
我开始实现几个函数来处理列表,例如 head
、tail
、init
、...
在某些时候,我查找了这些函数的实现以与我的进行比较,我偶然发现了 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
你不能只用你的解决方案来改变条款:
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 会告诉你你错过的每个地方。
我必须为大学学习 Haskell,因此我开始使用 learnyouahaskell.com。
我一直使用命令式语言,所以我决定练习 Haskell 比我用其他语言编写的代码多得多。
我开始实现几个函数来处理列表,例如 head
、tail
、init
、...
在某些时候,我查找了这些函数的实现以与我的进行比较,我偶然发现了 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
你不能只用你的解决方案来改变条款:
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 会告诉你你错过的每个地方。