How to get every Nth element of an infinite list in Haskell?

后端 未结 23 1279
再見小時候
再見小時候 2020-12-04 23:58

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

相关标签:
23条回答
  • 2020-12-05 00:55

    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..]]
    

    The Haskell 98 Report: Arithmetic Sequences

    0 讨论(0)
  • 2020-12-05 00:55

    (This was in response to a comment asking for a solution without drop)

    I couldn't see this solution, so:

    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
    

    works for finite and infinite list:

    take 15 (every 3 [1..])
    -- [3,6,9,12,15,18,21,24,27,30,33,36,39,42,45]
    
    0 讨论(0)
  • 2020-12-05 00:55

    It's more elegant to solve a related problem first: Keep every element whose index is divisible by n.

    everyNth n [] = []
    everyNth n (x:xs) = x : (everyNth n . drop (n-1)) xs
    

    And then to solve the example, use

    everyNthFirst n = everyNth n . drop (n-1)
    

    everyNthFirst 3 [5, 3, 0, 1, 8, 0, 3, 4, 0, 93, 211, 0 ...] gives [0, 0, 0, ...]

    0 讨论(0)
  • 2020-12-05 00:56

    Data.List.HT from utility-ht has sieve :: Int -> [a] -> [a].

    See documentation and 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
    
    0 讨论(0)
  • 2020-12-05 00:57

    An uglier, and more limited version of the accepted answer

    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
    

    For "line golfing" it can be written like this:

    every n xs = if rest == [] then [] else head rest : every n (tail rest) 
        where rest = drop (n-1) xs
    

    (It's more limited because it has an unnecessary Eq a constraint.)

    0 讨论(0)
提交回复
热议问题