Haskell声明空列表,但实际上不是空的?

时间:2017-07-05 11:23:51

标签: list haskell filter pattern-matching lazy-evaluation

我是Haskell的新手并且正在玩一下。我用守卫创建了一个递归函数。见下面的功能:

filterAge :: [Person] -> [String]
filterAge (x:xs)
 | (x:xs) == []                        = []
 | (getAge x) < 30 || (getAge x) > 40  = [] ++ filterAge xs
 | otherwise                           = [getName x] ++ filterAge xs

我有一个用10人创建的数据集,我在这个方法中使用它。当我尝试这个功能时,它给了所有合适的人,但之后它得到了一个非详尽的模式错误: ["Lise","Jaap","Elle","Ebba"*** Exception: D:\...:(44,1)-(47,77): Non-exhaustive patterns in function filterAge

我发现它永远不会到达第一个后卫。所以我玩了一下,发现了一些非常奇怪的东西(在我看来):

*Main> let (x:xs) = []
*Main> (x:xs) == []
False

现在我的主要问题是:(x:xs) == []为什么返回False?

如果有人有更好的方式让我做一个很棒的功能,但它并不是很重要。

提前致谢!

修改

感谢Willem Van Onsem和Lambda.xy.x我快速回答了我的问题。这导致以下功能完美运行:

filterAge :: [Person] -> [String]
filterAge []                           = []
filterAge (x:xs)
 | (getAge x) < 30 || (getAge x) > 40  = [] ++ filterAge xs
 | otherwise                           = [getName x] ++ filterAge xs

但是对于最好的版本,你必须检查Willem Van Onsem的答案。

2 个答案:

答案 0 :(得分:10)

列表定义为:

data [] a = [] | a : [a]

因此列表有两个构造函数:[]空列表,(x:xs)构造函数一个元素可以存储任意数量(零个或多个)剩余元素。

因此,(x:xs)是一个列表,其中包含至少一个元素xxs可以是一个空列表(因为它的类型为[a]),但x的类型为a,因此这是&#34; head &#34;的清单。您的let语句适用于模式匹配,由于空列表与(x:xs)不匹配,因此始终会失败。

另一个暗示是你的第一个后卫永远不能解雇。为了解决问题,您应该为空列表实现单独的案例。像:

filterAge :: [Person] -> [String]
filterAge []     = [] -- empty list case
filterAge (x:xs) -- first guard dropped
    | (getAge x) < 30 || (getAge x) > 40  = [] ++ filterAge xs
    | otherwise                           = [getName x] ++ filterAge xs

请注意,我们在第二个子句中删除了第一个保护,因为我们知道它总是会失败,因此检查它(可能)只会花费CPU周期。

我们仍然可以优化一些部分:

  1. 我们两次致电getAge,这是无用的,我们可以使用where子句对此进行优化;
  2. [] ++ somelist只是somelist:在空列表导致该列表后附加;和
  3. [element] ++ somelistelement : somelist,因为现在我们直接使用列表构造函数。
  4. 因此我们的filterAge可以改写为:

    filterAge :: [Person] -> [String]
    filterAge []     = [] -- empty list case
    filterAge (x:xs) | age < 30 || age > 40  = filterAge xs
                     | otherwise             = getName x : filterAge xs
        where age = getAge x

    请注意,如果使用-Wincomplete-patterns标志编译(或启动解释器)(警告表示不完整的模式),Haskell会自动警告您功能定义不完整,并且有没有定义子句的输入模式。

答案 1 :(得分:4)

回答你的“主要”问题,

*Main> let (x:xs) = [] -- (1)

表示当需要 xxs的值时,匹配 (x:xs) {{1} }和使用生成的绑定。匹配将从不成功,任何此类尝试将始终导致模式匹配失败错误

那为什么不呢?

[]

现在这意味着,尝试比较 *Main> (x:xs) == [] -- (2) (x:xs)。两者都是清单;比较列表涉及模式匹配顶部结构,然后递归地比较组件,成功 - 在失败时返回[] (实际上没有失败)。因此,False(_:_)之间的匹配尝试失败,导致作为比较结果的[]值立即返回

请注意, False x 绑定的在任何一点;因此没有错误,因为 xs (x:xs)[]的匹配从未被触发。