更具体地说,如何从现有的无限列表中生成每个第N个元素的新列表?
E.g。如果列表为[5, 3, 0, 1, 8, 0, 3, 4, 0, 93, 211, 0 ...]
,那么获取每个第3个元素将导致此列表[0,0,0,0,0 ...]
答案 0 :(得分:59)
我的版本使用drop:
every n xs = case drop (n-1) xs of
(y:ys) -> y : every n ys
[] -> []
编辑:这也适用于有限列表。仅限无限列表,Charles Stewart的解决方案略短。
答案 1 :(得分:35)
使用zip
等所有解决方案都会进行大量不必要的分配。作为一名功能性程序员,除非你真的需要,否则你要养成不分配的习惯。分配是昂贵的,与分配相比,其他一切都是免费的。并且分配不仅仅出现在您使用分析器找到的“热点”中;如果你不注意分配,它会杀死到处。
现在我赞同评论员认为可读性是最重要的事情。没有人想快速得到错误的答案。但它经常发生在函数式编程中,有多个解决方案,所有解决方案都具有相同的可读性,其中一些解决方案是分配的,而另一些则不是。建立寻找那些可读,非分配解决方案的习惯非常重要。
你可能认为优化器会摆脱分配,但你只有一半是正确的。 GHC是世界上最好的Haskell优化编译器,它确实设法避免为每个元素分配一对;它将filter
- zip
合成融入foldr2
。列表[1..]
的分配仍然存在。现在你可能不认为这是如此糟糕,但流融合,这是GHC目前的技术,是一个有点脆弱的优化。即使是专家也很难通过查看代码来准确预测它何时起作用。更一般的一点是当谈到像分配这样的关键属性时,你不想依赖一个你无法预测的结果的花哨的优化器。只要您能以同样可读的方式编写代码,最好不要再引入这些分配。
由于这些原因,我发现使用drop
的Nefrubyr解决方案是迄今为止最引人注目的解决方案。分配的唯一值正是那些必须成为最终答案一部分的缺点单元格(:
)。除此之外,drop
的使用使解决方案不仅仅是易于理解:它非常清晰,显然是正确的。
答案 2 :(得分:16)
我没有任何东西可以在工作中测试这个,但是像:
extractEvery m = map snd . filter (\(x,y) -> (mod x m) == 0) . zip [1..]
甚至可以在无限列表上工作。
(编辑:测试并更正。)
答案 3 :(得分:13)
从第一个元素开始:
everyf n [] = []
everyf n as = head as : everyf n (drop n as)
从第n个元素开始:
every n = everyf n . drop (n-1)
答案 4 :(得分:11)
MHarris的答案很棒。但我喜欢避免使用\
,所以这是我的高尔夫:
extractEvery n
= map snd . filter fst
. zip (cycle (replicate (n-1) False ++ [True]))
答案 5 :(得分:11)
编译器或解释器将计算步长(减去1,因为它基于零):
f l n = [l !! i | i <- [n-1,n-1+n..]]
答案 6 :(得分:10)
摆脱mod
的替代解决方案:
extractEvery n = map snd . filter ((== n) . fst) . zip (cycle [1..n])
答案 7 :(得分:10)
我前几天修改了我的Haskell版本,所以未经测试,但以下看起来比其他版本更简单(利用模式匹配和删除来避免zip和mod):
everynth :: Int -> [a] -> [a]
everynth n xs = y : everynth n ys
where y : ys = drop (n-1) xs
答案 8 :(得分:4)
另一种方法:
takeEveryM m lst = [n | (i,n) <- zip [1..] lst, i `mod` m == 0]
又一个:
import Data.List
takeEveryMth m =
(unfoldr g) . dr
where
dr = drop (m-1)
g (x:xs) = Just (x, dr xs)
g [] = Nothing
答案 9 :(得分:2)
使用视图模式!
{-# LANGUAGE ViewPatterns #-}
everynth n (drop (n-1) -> l)
| null l = []
| otherwise = head l : everynth n (tail l)
保留了Nefrubyr的丑陋版本的答案,所以评论是有道理的。
everynth :: Int -> [a] -> [a]
everynth n l = case splitAt (n-1) l of
(_, (x:xs)) -> x : everynth n xs
_ -> []
答案 10 :(得分:2)
明确的递归是邪恶的!使用库构造,如地图,过滤,折叠,扫描,再现,迭代等。除非它使代码更容易阅读,即使对于那些使用库的人来说,它只是使你的代码更少模块化,更冗长,更难以推理。
严重的是,明确的递归版本需要被简化为负面的任务,就像这样简单。现在我想我应该说一些有建设性的东西来平衡我的烦恼。
我更喜欢使用地图:
每n xs = map(xs !!)[n-1,n-1 + n ..]
而不是ja的列表理解,所以读者不必担心我是什么。但无论哪种方式,当它可能是O(n)时它是O(n ^ 2),所以也许这样更好:
每个n =地图(!!(n-1))。迭代(drop n)
答案 11 :(得分:1)
extractEvery n l = map head (iterate (drop n) (drop (n-1) l))
我会为自己感到骄傲,直到我看到格雷格得到了同样的答案并且在我之前
答案 12 :(得分:1)
(这是对a comment要求无下落的解决方案的回应)
我看不到这个解决方案,所以:
every _ [] = []
every n (x:xs) = every' n (n-1) (x:xs)
where every' n c [] = []
every' n 0 (x:xs) = x : every' n (n-1) xs
every' n c (x:xs) = every' n (c-1) xs
适用于有限和无限列表:
take 15 (every 3 [1..])
-- [3,6,9,12,15,18,21,24,27,30,33,36,39,42,45]
答案 13 :(得分:1)
带有foldr
的愚蠢版本:
everyNth n l = foldr cons nil l (n-1) where
nil _ = []
cons x rest 0 = x : rest n
cons x rest n = rest (n-1)
答案 14 :(得分:0)
这似乎稍微好一点展开:
everyNth :: Int -> [a] -> [a]
everyNth n = unfoldr g
where
g [] = Nothing
g xs = let (ys, zs) = splitAt n xs in Just (head ys, zs)
甚至更好,使用Data.List.Spit:
everyNth n = (map head) . chunksOf n
答案 15 :(得分:0)
旧问题,但我会发布我想出的内容:
everyNth :: [a] -> Int -> [a]
everyNth xs n = [snd x | x <- (zip [1..] xs), fst x `mod` n == 0]
我把list参数放在Int
之前,这样我就可以把这个函数和一个列表放在Int
的列表上,如果需要的话。
答案 16 :(得分:0)
首先解决一个相关的问题会更优雅:保留索引可被n整除的每个元素。
everyNth n [] = []
everyNth n (x:xs) = x : (everyNth n . drop (n-1)) xs
然后使用
解决该示例everyNthFirst n = everyNth n . drop (n-1)
everyNthFirst 3 [5, 3, 0, 1, 8, 0, 3, 4, 0, 93, 211, 0 ...]
给出[0, 0, 0, ...]
答案 17 :(得分:0)
Data.List.HT
的 utility-ht
具有sieve :: Int -> [a] -> [a]
。
请参见documentation和source:
{-| keep every k-th value from the list -}
sieve, sieve', sieve'', sieve''' :: Int -> [a] -> [a]
sieve k =
unfoldr (\xs -> toMaybe (not (null xs)) (head xs, drop k xs))
sieve' k = map head . sliceVertical k
sieve'' k x = map (x!!) [0,k..(length x-1)]
sieve''' k = map head . takeWhile (not . null) . iterate (drop k)
propSieve :: Eq a => Int -> [a] -> Bool
propSieve n x =
sieve n x == sieve' n x &&
sieve n x == sieve'' n x
答案 18 :(得分:0)
一个丑陋的,接受答案的更有限版本
every :: Eq a => Int -> [a] -> [a]
every n xs = if rest == []
then []
else head rest : every n (tail rest)
where rest = drop (n-1) xs
对于“打高尔夫球”,可以这样写:
every n xs = if rest == [] then [] else head rest : every n (tail rest)
where rest = drop (n-1) xs
(它更有限,因为它有一个不必要的Eq a
约束。)
答案 19 :(得分:0)
还有一个非常实用的功能,没有明显的条件:
everyNth :: Int -> [a] -> [a]
everyNth 0 = take 1
everyNth n = foldr ($) [] . zipWith ($) operations where
operations = cycle $ (:) : replicate (n-1) (const id)
(请注意,此元素采用索引为n的倍数的每个元素。该元素与原始问题略有不同,但易于转换。)
答案 20 :(得分:0)
我们可以使用列表理解:
takeEvery :: Int -> [a] -> [a]
takeEvery n xs = [x | (x, i) <- zip xs [1..], i `mod` n == 0]
在所有值索引对(x,i)中,如果i被n整除,则取x。请注意,索引从此处开始。
答案 21 :(得分:-1)
我的解决方案是:
every :: Int -> [a] -> [[a]]
every _ [] = []
every n list = take n list : (every n $ drop n list)
它不使用zip,但我无法分辨它的性能和内存配置文件。