我是一名C#人,试图从Erik Meijer的第9频道网络广播中自学Haskell。我遇到了一个有趣的谜题,其中包括使用zip和mod跳过列表中的每个'n'元素。
every :: Int -> [a] -> [a]
every _ [] = []
every n xs = [x | (x,i) <- zip xs [1..], i `mod` n == 0]
如果我们可以避免使用mod,我一直认为它可能更有效(对于非常大的列表或流)。
我考虑过懒惰地创建一个重复的整数列表,这样我们就可以简单地将i的值与n进行比较。
repeatInts :: Int -> [Int]
这样调用repeatInts 3
无限回复[1,2,3,1,2,3,1,2,3,1,2,3,..]
。
鉴于此,我们可以像这样重新定义every
:
every :: Int -> [a] -> [a]
every _ [] = []
every n xs = [x | (x,i) <- zip xs (repeatInts n), i == n]
所以我的问题是:你将如何实施repeatInts
?
答案 0 :(得分:20)
使用cycle
:
cycle :: [a] -> [a]
cycle
将有限列表绑定到循环列表中,或等效地,将原始列表无限重复。这是无限名单上的身份。
您可以使用repeatInts
:
cycle
*Main> let repeatInts n = cycle [1..n]
*Main> :t repeatInts
repeatInts :: (Num t, Enum t) => t -> [t]
*Main> take 10 $ repeatInts 3
[1,2,3,1,2,3,1,2,3,1]
对于好奇,GHC使用
实现cycle
cycle [] = errorEmptyList "cycle"
cycle xs = xs' where xs' = xs ++ xs'
在纯粹的功能性说法中,这种奇怪的技术被称为<绑定结,它创建循环数据结构而不是无限数据结构。
详见
[tying-the-knot]
答案 1 :(得分:2)
迟到的答案,但它也可以这样写:
repeatInts :: Int -> [Int]
repeatInts 0 = []
repeatInts a = [1..a] ++ repeatInts a