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

Sna*_*ake 4 haskell list 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
Run Code Online (Sandbox Code Playgroud)

我有一个用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
Run Code Online (Sandbox Code Playgroud)

现在我的主要问题是:为什么(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
Run Code Online (Sandbox Code Playgroud)

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

Wil*_*sem 10

列表定义为:

data [] a = [] | a : [a]
Run Code Online (Sandbox Code Playgroud)

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

因此(x:xs)是一个至少包含一个元素的列表:x.该xs可以是一个空列表(因为它具有类型[a]),但x已键入a所以这是" 列表".您的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
Run Code Online (Sandbox Code Playgroud)

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

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

  1. 我们调用getAge两次,这是没用的,我们可以使用一个where子句来优化它;
  2. [] ++ somelist简单地说somelist:在空列表后附加结果列表; 和
  3. [element] ++ somelist是的element : somelist,因为现在我们直接使用列表构造函数.

所以我们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
Run Code Online (Sandbox Code Playgroud)

请注意,如果使用-Wincomplete-patterns标志编译(或启动解释器)(不完整模式的警告),Haskell将自动警告您函数定义未完成,并且存在未定义子句的输入模式.