如何用Haskell列表推导式生成列表[1,2,5,6,9,10,13,14,17,18]?
解决Haskell列表推导式生成目标列表的问题
你的问题很典型——列表推导式里的生成器和过滤条件搞混了,导致结果重复。先帮你理清楚:
为什么你的代码会得到重复结果?
你写的代码:
[x | x <- [1..18], y <- [1..4], y < 3]
这里的y <- [1..4]是生成器,意味着对于每个x,Haskell会遍历y的所有值(1、2、3、4),然后筛选出y < 3的情况(也就是y=1和y=2)。所以每个x会被生成两次,最终结果里每个数都重复了,这显然不是你想要的。
用列表推导式生成目标列表的正确方式
首先先看目标列表[1,2,5,6,9,10,13,14,17,18]的规律:每4个数里取前两个(1-4取1、2,5-8取5、6,以此类推)。基于这个规律,我们可以直接在过滤条件里判断x是否符合这个规则。
方法1:用取模运算过滤
最直接的写法是检查x除以4的余数是1或2:
[x | x <- [1..18], x `mod` 4 `elem` [1, 2]]
或者换一种更简洁的取模判断:
[x | x <- [1..18], (x - 1) `mod` 4 < 2]
这两种写法都能精准筛选出你要的数,运行后就能得到目标列表。
方法2:用分组思路(结合列表推导式)
如果你想更直观地体现“每4个取前两个”的逻辑,也可以先把列表分成每4个一组,再取每组前两个,最后拼接。虽然用到了concat,但核心还是列表推导式:
concat [take 2 group | group <- chunksOf 4 [1..18]]
不过这个需要先导入Data.List.Split模块(import Data.List.Split),如果不想额外导入模块,方法1更合适。
额外:如果一定要用y辅助过滤(不推荐,但可以演示)
如果你坚持想用y来参与过滤逻辑(虽然没必要),可以把y的判断放在过滤条件里,而不是作为生成器。比如:
[x | x <- [1..18], any (\y -> y < 3) [1..(x `mod` 4)]]
但这种写法可读性差,不如直接用取模的方式清晰,所以不推荐。
内容的提问来源于stack exchange,提问作者mdasilva
相关产品推荐
相关产品推荐

