PushedCrayon
PushedCrayon

Reputation: 123

How to create Haskell function that returns every third element from a list of ints

I want to create a function that returns every third int from a list of ints without using any predefined functions. For example, everyThird [1,2,3,4,5] --> [1,4]

everyThird:: [a] -> [a]

Could I just continue to iterate over the list using tail and appending to a new list every third call? I am new to Haskell and very confused with all of this

Upvotes: 0

Views: 2537

Answers (5)

dfeuer
dfeuer

Reputation: 48631

One classic approach:

everyThird xs = [x | (1,x) <- zip (cycle [1..3]) xs]

Upvotes: 5

RoadRunner
RoadRunner

Reputation: 26335

You can also use chunksOf from Data.List.Split to seperate the lists into chunks of 3, then just map the first element of each:

import Data.List.Split

everyThird :: [a] -> [a]
everyThird xs = map head $ chunksOf 3 xs

Which works as follows:

*Main> everyThird [1,2,3,4,5]
[1,4]

Note: You may need to run cabal install split to use chunksOf.

Upvotes: 2

Chai T. Rex
Chai T. Rex

Reputation: 3006

One other way of doing this is to handle three different base cases, in all of which we're at the end of the list and the list is less than three elements long, and one recursive case, where the list is at least three elements long:

everyThird :: [a] -> [a]
everyThird []         = []
everyThird [x]        = [x]
everyThird [x, _]     = [x]
everyThird (x:_:_:xs) = x:everyThird xs

Upvotes: 9

5ar
5ar

Reputation: 2210

Haskell doesn't have classical iteration (i.e. no loops), at least not without monads, but you can use similar logic as you would in a for loop by zipping your list with indexes [0..] and applying appropriate functions from Data.List.

E.g. What you need to do is filter every third element:

everyThirdWithIndexes list = filter (\x -> snd x `mod` 3 == 0) $ zip list [0..]

 

Of course you have to get rid of the indexes, there are two elegant ways you can do this:

everyThird list = map (fst) . everyThirdWithIndexes list
-- or:
everyThird list = fst . unzip . everyThirdWithIndexes list

 

If you're not familiar with filter and map, you can define a simple recursion that builds a list from every first element of a list, drops the next two and then adds another from a new function call:

everyThird [] = []  -- both in case if the list is empty and the end case
everyThird (x:xs) = x : everyThird (drop 2 xs)

 

EDIT: If you have any questions about these solutions (e.g. some syntax that you are not familiar with), feel free to ask in the comments. :)

Upvotes: 5

Silvio Mayolo
Silvio Mayolo

Reputation: 70367

You want to do exactly what you said: iterate over the list and include the element only on each third call. However, there's a problem. Haskell is a funny language where the idea of "changing" a variable doesn't make sense, so the usual approach of "have a counter variable i which tells us whether we're on the third element or not" won't work in the usual way. Instead, we'll create a recursive helper function to maintain the count for us.

everyThird :: [Int] -> [Int]
everyThird xs = helper 0 xs
  where helper _ [] = []
        helper 0 (x : xs) = x : helper 2 xs
        helper n (_ : xs) = helper (n - 1) xs

We have three cases in the helper.

  1. If the list is empty, stop and return the empty list.
  2. If the counter is at 0 (that is, if we're on the third element), make a list starting with the current element and ending with the rest of the computation.
  3. If the counter is not at zero, count down and continue iteration.

Because of the way pattern matching works, it will try these three statements in order.

Notice how we use an additional argument to be the counter variable since we can't mutate the variable like we would in an imperative language. Also, notice how we construct the list recursively; we never "append" to an existing list because that would imply that we're mutating the list. We simply build the list up from scratch and end up with the correct result on the first go round.

Upvotes: 5

Related Questions