哈斯克尔守卫没有得到满足

时间:2010-09-08 01:58:06

标签: haskell guard

test :: [String] -> [String]

test = foldr step []

  where step x ys

          | elem x ys = x : ys

          | otherwise = ys

我正在尝试构建一个包含所有输入的不同字符串的新列表。我的测试数据是:

test ["one", "one", "two", "two", "three"]

预期结果:

["one", "two", "three"]

我是Haskell的新手,我确信我错过了一些非常基础和明显的东西,但是已经没有办法探索这个问题了。你能指出我的想法不足的地方吗?

实际回复是[]。似乎永远不会遇到第一个保护条件(如果我用True替换它,原始列表被复制),所以输出列表永远不会被构建。

我的理解是,折叠会累积列表中每个项目的步骤结果,并将其添加到空列表中。我预计该步骤将测试每个项目是否包含在输出列表中(测试的第一个元素不在那里)并将添加尚未包含在输出列表中的任何内容。显然不是: - )

2 个答案:

答案 0 :(得分:7)

您的推理是正确的:您只需要切换= x : ys= ys,以便在 {{1}的元素时添加x }}。此外,ys完成了这一切。

答案 1 :(得分:7)

考虑一下:你的代码在说“当x在余数中时,在结果前面加x”,即创建副本。您只需要将其更改为“当x不在余数中时,将x加到结果之前”并获得正确的函数。

此功能与Data.List.nub的重要区别在于:此功能更严格。因此:

test [1..] = _|_   -- infinite loop (try it)
nub [1..] = [1..]

nub正确地给出了无限列表的答案 - 这意味着它不需要整个列表来启动计算结果,因此它是流处理游戏中的一个不错的播放器。

它是严格的原因是因为elem是严格的:它在返回结果之前搜索整个列表(假设它没有找到匹配项)。你可以这样写:

nub :: (Eq a) => [a] -> [a]
nub = go []
    where
    go seen [] = []
    go seen (x:xs) | x `elem` seen = go seen xs
                   | otherwise     = x : go (x:seen) xs

注意seen如何像输出一样增长,而你的增长就像输出的剩余部分。前者始终是有限的(从[]开始并一次添加一个),而后者可能是无限的(例如[1..])。因此,这种变体可以更懒惰地产生元素。

如果您使用Data.Set而不是seen的列表,则会更快(O(n log n)而不是O(n ^ 2))。但它增加了Ord约束。