【问题标题】:Haskell guards not being met没有遇到 Haskell 警卫
【发布时间】:2011-04-09 12:17:00
【问题描述】:
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,则会复制原始列表),因此永远不会构建输出列表。

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

【问题讨论】:

  • 谢谢特拉维斯。正如我所说 - 一些完全明显的遗漏。
  • @Travis:建议你把你的评论变成一个答案,这样这个问题就不会在没有答案的堆里漂浮,直到天结束。此外,免费代表将是一个加号。 @Chris:如果您在每行的开头添加额外的四个空格,您的代码将正确呈现。
  • @Jack:我已经修复了代码格式。
  • @Jack - 感谢您整理格式并告知我如何正确呈现它。 @Travis - 感谢您提出这个答案。

标签: haskell guard


【解决方案1】:

想一想:您的代码是在说“当 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 约束。

【讨论】:

  • 我不断回复此回复 - 随着我继续尝试学习 Haskell,它让我对 Haskell 的力量有了更深入的了解。
【解决方案2】:

您的推理是正确的:您只需要切换= x : ys= ys 以便在x 不是ys 的元素时添加它。此外,Data.List.nub 也正是这样做的。

【讨论】:

    猜你喜欢
    • 2014-06-19
    • 1970-01-01
    • 1970-01-01
    • 2014-01-13
    • 2011-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-30
    相关资源
    最近更新 更多