views:

985

answers:

13

More specifically, how do I generate a new list of every Nth element from an existing infinite list?

E.g. if the list is [5, 3, 0, 1, 8, 0, 3, 4, 0, 93, 211, 0 ...] then getting every 3rd element would result in this list [0,0,0,0,0 ...]

+13  A: 

I don't have anything to test this with at work, but something like:

extractEvery m = map snd . filter (\(x,y) -> (mod x m) == 0) . zip [1..]

should work even on infinite lists.

(Edit: tested and corrected.)

MHarris
Whoops. Thanks!
MHarris
Thanks, this works well, although I don't quite understand it yet (I'm still a beginner in Haskell).
opert
+7  A: 

MHarris's answer is great. But I like to avoid using \, so here's my golf:

extractEvery n
  = map snd . filter fst
  . zip (cycle (True : replicate (n-1) False))
yairchu
Or `cycle $ replicate (n-1) False ++ [True]` since OP wants to start at index `n-1`, not index `0`.
ephemient
Yes, ephemient's modification does what I originally asked for.
opert
+6  A: 

Alternate solution to get rid of mod:

extractEvery n = map snd . filter ((== n) . fst) . zip (cycle [1..n])
Alexey Romanov
My fav solution :D
trinithis
+4  A: 

I nuked my version of Haskell the other day, so untested, but the following seems a little simpler than the others (leverages pattern matching and drop to avoid zip and mod)

everynth n xs = y:(everynth n ys) where y:ys = drop (n-1) xs

Charles Stewart
I started with this, and then tested it: it fails for finite lists when (y:ys) cannot match the empty list. But the question is for infinite lists so I guess that's OK!
Nefrubyr
Indeed, this works fine for infinite lists, but not for finite lists. I like how simple the solution looks -- if only it worked for finite lists as well...
opert
+19  A: 

My version using drop:

every n xs = case drop (n-1) xs of
              (y:ys) -> y : every n ys
              [] -> []

Edit: this also works for finite lists. For infinite lists only, Charles Stewart's solution is slightly shorter.

Nefrubyr
Hah! For finite lists too, mine is shorter. It just doesn't work...
Charles Stewart
Cool, this works well. Thanks.
opert
+7  A: 

Starting at the first element:

everyf n [] = []
everyf n as  = head as : everyf n (drop n as)

Starting at the nth element:

every n = everyf n . drop (n-1)
sth
Taking this to the logical next step, `everyf n = map head . takeWhile (not . null) . iterate (drop n)` or `every n = map head . takeWhile (not . null) . iterate (drop n) . drop (n-1)`.
ephemient
`. drop (n-1)` instead of `(drop (n-1) as)` makes it so much nicer and more readable that I now just had to use that in my answer... :)
sth
Very nice. I like how you gave two functions to get either [0, N1, N2, N3...] or [N1, N2, N3...].
opert
+9  A: 

All the solutions using zip and so on do tons of unnecessary allocations. As a functional programmer, you want to get into the habit of not allocating unless you really have to. Allocation is expensive, and compared to allocation, everything else is free. And allocation doesn't just show up in the "hot spots" you would find with a profiler; if you don't pay attention to allocation, it kills you everywhere.

Now I agree with the commentators that readability is the most important thing. Nobody wants to get wrong answers fast. But it happens very often in functional programming that there are multiple solutions, all about equally readable, some of which allocate and some of which do not. It's really important to build a habit of looking for those readable, non-allocating solutions.

You might think that the optimizer would get rid of allocations, but you'd only be half right. GHC, the world's best optimizing compiler for Haskell, does manage to avoid allocating a pair per element; it fuses the filter-zip composition into a foldr2. The allocation of the list [1..] remains. Now you might not think this is so bad, but stream fusion, which is GHC's current technology, is a somewhat fragile optimization. It's hard even for experts to predict exactly when it's going to work, just by looking at the code. The more general point is that when it comes to a critical property like allocation, you don't want to rely on a fancy optimizer whose results you can't predict. As long as you can write your code in an equally readable way, you're much better off never introducing those allocations.

For these reason I find Nefrubyr's solution using drop to be by far the most compelling. The only values that are allocated are exactly those cons cells (with :) that must be part of the final answer. Added to that, the use of drop makes the solution more than just easy to read: it is crystal clear and obviously correct.

Norman Ramsey
I agree, and I find Nefrubyr's solution easier to follow as well.
opert
I'm sure a good compiler will avoid the unnessary allocations.
trinithis
-1, i would recommend coding for readability then profiling then optimizing the profiled portions. No point in making code readability sacrifices when it may not even cause a problem
RCIX
sth's version has this property too.
Charles Stewart
@trinithis: I checked with GHC, and you are mostly right. But you make a good point, so I have edited my answer to make it clear why you don't necessarily want to throw an optimizer at some code and hope for the best.
Norman Ramsey
@RCIX: I agree with you, but only in part. Certainly readability is paramount. But if you ignore allocation *everywhere*, then you will slow down *all* of your program, and there won't necessarily be "hot spots" that you can find easily by profiling. I've updated my asnwer to try to reflect a more nuanced view.
Norman Ramsey
Wrt. readability: it is not only about knowing how inouts are transformed into outputs, it is also about knowing how the machine will do this. Prolog is lovely for making it clear what problem is being solved. But acquiring a feel for what happens when you run the code...
Charles Stewart
+2  A: 

Another way of doing it:

takeEveryM m lst = [n | (i,n) <- zip [1..] lst, i `mod` m == 0]

Yet Another:

import Data.List

takeEveryMth m = 
  (unfoldr g)  . dr
     where 
       dr = drop (m-1)
       g (x:xs) = Just (x, dr xs)
       g []     = Nothing
primodemus
A: 

the compiler or interpreter will compute the step size (subtract 1 since it's zero based):

f l n = [l !! i | i <- [n-1,n-1+n..] ]

http://www.haskell.org/onlinereport/exps.html#arithmetic-sequences

ja
+1  A: 

Use a view pattern!

{-# LANGUAGE ViewPatterns #-}

everynth n (drop (n-1) -> l)
  | null l = []
  | otherwise = head l : everynth n (tail l)

Ugly version of Nefrubyr's answer preserved so comments make sense.

everynth :: Int -> [a] -> [a]
everynth n l = case splitAt (n-1) l of
                 (_, (x:xs)) -> x : everynth n xs
                 _           -> []
Greg Bacon
Seems rather pointless to use `splitAt` instead of `drop` if you never even look at the `fst` of the resulting tuple.
ephemient
I see now that it's a muddier version of Nefrubyr's answer.
Greg Bacon
Go ahead and remove it if you want; anybody confused can look through edit history, or I can delete my comment. That aside: documentation for `ViewPatterns` says that the implementation shall tries to gather common views together for execution efficiency, so I think the guards are just confounding. Subjective, of course...
ephemient
Maybe it'll spare someone thinking, 'No one's used `splitAt` yet!' I agree that the guards aren't ideal, but the alternatives aren't great either: I'd have to repeat the same code in separate cases or define a helper function at the outer scope—nicer if cases of a function could share a `where` clause.
Greg Bacon
A: 

Explicit recursion is evil! Use a library construct like map, filter, fold, scan, reproduce, iterate etc instead. Unless it makes the code drastically easier to read even to those au fait with the libraries, then it's just making your code less modular, more verbose and harder to reason about.

Seriously, the explicitly recursive versions need to be voted down to negative for a task as simple as this. Now I guess I should say something constructive to balance my carping.

I prefer to use a map:

every n xs = map (xs!!) [n-1,n-1+n..]

rather than the ja's list comprehension so the reader doesn't have to worry about what i is. But either way it's O(n^2) when it could be O(n), so maybe this is better:

every n = map (!!(n-1)) . iterate (drop n)

Greg M
I did try to rewrite my answer as a map or fold but in this case I found the explicit recursion cleanest. As a general principle I agree with you that library functions such as maps and folds are preferred. primodemus's answer using unfoldr is the sort of thing I was aiming for, but I'm glad I stopped where I did ;-) Your solution walks each section of the list twice (once for `!!`, once for `drop`); ephemient's comment on sth's answer gives the best solution along these lines by "sliding" the list along and using `map head`.
Nefrubyr
Missed that comment! Was a toss-up whether to post that or mine, decided simpler code was more important than a constant factor.People frequently do find explicit recursion clearer before they're used to the libraries, but the way to get used to the libraries is to use them. Voting up explicitly-recursive solutions to something like this is a good way to _prevent_ them learning to do it right.
Greg M
A: 

extractEvery n l = map head (iterate (drop n) (drop (n-1) l))

I was going to feel proud of myself, until I saw that Greg got about the same answer and before me

Ellery Newcomer