为啥使用 null 函数而不是 == [] 来检查 Haskell 中的空列表?

Posted

技术标签:

【中文标题】为啥使用 null 函数而不是 == [] 来检查 Haskell 中的空列表?【英文标题】:Why use null function instead of == [] to check for empty list in Haskell?为什么使用 null 函数而不是 == [] 来检查 Haskell 中的空列表? 【发布时间】:2019-11-23 12:53:44 【问题描述】:

我正在阅读Learn You a Haskell for Great Good! 的“开始”一章。它说:

null 检查列表是否为空。如果是,则返回 True,否则返回 False。使用此函数代替 xs == [](如果您有一个名为 xs 的列表)

我在 ghci 中试过:

xs = []      -- and then,

xs == []
null xs

他们都是True

我想知道有什么区别。

我应该使用null 函数而不是== [] 吗?为什么?

【问题讨论】:

【参考方案1】:

您应该使用null。在大多数情况下,这无关紧要,但无论如何养成一个好习惯,因为有时您可能想检查不可比较的事物列表是否为空。这是一个简短明了的示例,显示了这种差异:

> null [id]
False
> [id] == []
<interactive>:1:1: error:
    • No instance for (Eq (a0 -> a0)) arising from a use of ‘==’
        (maybe you haven't applied a function to enough arguments?)
    • In the expression: [id] == []
      In an equation for ‘it’: it = [id] == []

【讨论】:

“在大多数情况下,这并不重要”——我不同意。编写多态列表函数是很常见的,它不需要Eq 约束(即使在大多数情况下它们确实会实例化Eq 类型)。 我同意这个答案中写的所有内容。为了后代和完整性,还可能值得一提的是为什么length xs == 0 是个坏主意。 记录一下:length xs == 0 是个坏主意,因为它需要遍历整个链表(即O(n),并且永远不会在无限链表上终止)。【参考方案2】:

有区别。为了使用x == [],列表元素的类型应该是Eq 类型类的成员。事实上,检查两个列表的相等性是由实例声明定义的:

instance Eq a => Eq [a] where
    []     == []      =  True
    (x:xs) == (y:ys)  =  x == y  &&  xs == ys
    _      == _       =  False

这意味着如果xIO Ints 的列表,则不能使用x == []

null :: [a] -&gt; Bool 另一方面,使用模式匹配。这是implemented as:

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

所以无论列表的元素是什么类型,它都会一直进行类型检查。

【讨论】:

【参考方案3】:

除了到目前为止给出的好答案之外,null 实际上还有类型

null :: Foldable t => t a -> Bool

我不知道您是否了解过 LYAH 中的类型类,但不足之处在于 null 不仅可以用于列表,还可以用于实现 null 的任何数据结构。

也就是说,在MapSet 上使用null 也是有效的。

> null Map.empty
True
> null (Map.singleton 1)
False
> null Set.empty
True
> null (Set.singleton 1)
False
> null []
True
> null [1]
False

我认为编写需要如此通用的函数并不常见,但默认编写更通用的代码也无妨。

附注

在许多情况下,您最终会想要使用像null 这样的函数来对列表(或其他数据结构)执行条件行为。如果您已经知道您的输入是一个特定的数据结构,那么只对其空大小写进行模式匹配会更优雅。

比较

myMap :: (a -> b) -> [a] -> [b]
myMap f xs
  | null xs = []
myMap f (x:xs) = f x : myMap f xs

myMap' :: (a -> b) -> [a] -> [b]
myMap' f [] = []
myMap' f (x:xs) = f x : myMap' f xs

一般来说,如果有意义的话,您应该尝试更喜欢模式匹配。

【讨论】:

【参考方案4】:

过滤所有空列表的简单函数也会失败:

withoutEmpty = filter (== [])

那就是:

withoutEmpty = filter null

注意:

withoutEmpty ls = filter (==[]) ls

会正常工作,但重要的一点是,在某些情况下,像另一种可能会失败。

另请查看@cole 答案,它补充了此处的所有答案,类型类 Foldable 具有要实现的null 函数:

查看Foldable here的更多信息

【讨论】:

这个答案有点误导。 withoutEmpty = filter (==[]) 可能会导致错误,但 withoutEmpty x = filter (==[]) x 工作正常。 @DanielWagner(或 Damian)能否详细说明为什么只有无点版本无法进行类型检查? @LeifWillerts What is the monomorphism restriction?

以上是关于为啥使用 null 函数而不是 == [] 来检查 Haskell 中的空列表?的主要内容,如果未能解决你的问题,请参考以下文章

为啥要检查 (*argv == NULL)? [复制]

为啥 Theme.of(context) 告诉我在取消引用之前检查该值是不是为“null”?

我正在尝试登录并检查用户是不是存在,但我的执行标量一直返回 null,我不知道为啥?

为啥你可以在函数中使用另一种方法而不是赋值运算符来更改 Ruby 中局部变量的值?

为啥我的 jwt 令牌在背面而不是正面返回 null

为啥使用 NGRX 而不是构造函数注入服务?