list - Haskell 声明空列表,但实际上哪个不是空的?

标签 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 的答案。

最佳答案

列表定义为:

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

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

因此 (x:xs) 是一个列表 ,其中至少有一个元素 : xxs 可以是一个空列表(因为它的类型是 [a] ),但 x 的类型是 a 所以它是列表的“头”。您的 let 语句与 模式匹配 一起使用,并且由于空列表无法与 (x:xs) 匹配,因此它总是会失败。

另一个含义是您的 第一 guard 永远不能触发 。为了解决这个问题,您应该为空列表实现一个单独的案例。喜欢:
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 周期。

还有一些地方我们可以优化:
  • 我们调用了两次 getAge ,没用,我们可以用一个 where 子句来优化这个;
  • [] ++ somelist 只是 somelist :在一个空列表之后追加导致该列表;和
  • [element] ++ somelistelement : 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

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

    关于list - Haskell 声明空列表,但实际上哪个不是空的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44924897/

    相关文章:

    javascript - 如何使用 JScript 或 JavaScript 创建数组文字?

    r - 使用 %in% 通过列表过滤多列并在 R 中过滤

    r - 根据另一个 tibble 过滤 mutate 中的一个 tibble?

    parsing - Haskell 中的通用自下而上解析器组合器

    haskell - (->) 是否有数据构造函数?

    javascript - 如何过滤对象数组并仅返回一个结果而不带数组括号

    r - 将 sf 对象列表转换为一个 sf

    c# - 如何列出使用 .asmx Web 服务将项目添加到 Sharepoint 列表

    python - 如何从多个句子中返回列表中的关键字?

    c++ - 错误 : does not name a type (c++)