Haskell 哈斯凯尔卫兵没有被会见
我正在尝试建立一个新的列表,其中包含输入的所有不同字符串。我的测试数据是:Haskell 哈斯凯尔卫兵没有被会见,haskell,guard,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"] 我是哈斯克尔的新手,我确信我遗漏了一些非常基本和明显的东
test :: [String] -> [String]
test = foldr step []
where step x ys
| elem x ys = x : ys
| otherwise = ys
预期结果:
test ["one", "one", "two", "two", "three"]
我是哈斯克尔的新手,我确信我遗漏了一些非常基本和明显的东西,但已经没有办法去探索这一点。你能指出我的思维缺陷吗
实际响应是[]
。似乎从未满足第一个保护条件(如果我将其替换为True
,则会复制原始列表),因此从未构建输出列表
我的理解是折叠将累积列表中每个项目的步骤结果,并将其添加到空列表中。我预计该步骤将测试每个项目是否包含在输出列表中(测试的第一个元素不在那里),并将添加任何尚未包含到输出列表中的内容。显然不是:-)您的推理是正确的:您只需要切换
=x:ys
和=ys
,这样您就可以在x
不是ys
的元素时添加x
。另外,Data.List.nub
做的正是这件事。想想看:你的代码是说“当x在余数中时,在x前面加上结果”,即创建一个副本。您只需将其更改为“当x不在余数中时,在x前面加上结果”,就可以得到正确的函数
此函数与Data.List.nub
有一个重要区别:此函数更严格。因此:
["one", "two", "three"]
nub
正确地给出了无限列表的答案——这意味着它不需要整个列表来开始计算结果,因此它是流处理游戏中的好玩家
它之所以严格是因为elem
是严格的:它在返回结果之前搜索整个列表(假定没有找到匹配项)。你可以这样写:
test [1..] = _|_ -- infinite loop (try it)
nub [1..] = [1..]
请注意,到目前为止,seen
的增长方式与输出的增长方式类似,而您的增长方式与输出的其余部分类似。前者总是有限的(从[]
开始,一次添加一个),而后者可能是无限的(例如[1..]
)。因此,这种变体可以更惰性地生成元素
如果使用
数据。设置而不是查看的列表,则速度会更快(O(n log n)而不是O(n^2))。但它增加了一个约束条件。谢谢特拉维斯。正如我所说的——显然遗漏了一些东西。@Travis:我建议你把你的评论变成一个答案,这样这个问题在几天结束之前都不会在未回答的问题堆中出现。此外,免费代表将是一个加号@克里斯:如果你在每行的开头多加四个空格,你的代码就会正确呈现。@杰克:我已经修复了代码格式。@杰克-谢谢你整理了格式并告诉我如何正确呈现它@特拉维斯-谢谢你回答这个问题。我一直在回复这个问题-当我继续努力学习哈斯凯尔时,它让我对哈斯凯尔的力量有了更深的了解。
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