Reputation: 4402
I'm looking for a way to do something like this. Say we start with 1. For every number odd number we add it +5 to the end of the list. For every even number we add it +3 and it +7 to the end of the list. The list would look like this
[1, 6, 9, 13, 14, 18, 17, 21, 21, 25,...]
How would I go about doing something like this in Haskell?
I'm aware that you can make a list where each element depends on the previous ones, but how do you do it when more than one element depends on the same one, and you don't know which group depends on which unless you start from the beginning?
Upvotes: 7
Views: 225
Reputation: 18189
The following solution uses a self-referencing lazy list, and so doesn't have the linear time problem of the solution by @GaneshSittampalam:
xs = 1 : concatMap extras xs where
extras x
| odd x = [x+5]
| otherwise = [x+3,x+7]
Upvotes: 13
Reputation: 29100
It's quite similar to how you would do it if only a single element was being generated at a time. In that case you would pass down a single value to keep track of the "seed" for the next elements. In this situation you can just pass down a list instead:
xs = process [1]
-- notice no [] case as we don't expect it to be needed
process (n:ns) = n:process (ns ++ extras)
where
extras
| n `mod` 2 == 0 = [n+3, n+7]
| otherwise = [n+5]
The general idea is that the argument to process
contains all the numbers that we know should be in the output list, but for which we haven't yet added the "consequent" numbers. To make progress, we inspect the first of those, "output" it by putting it at the head of the result, and calculate the consequent numbers and put those into the list that's waiting to be processed. By putting them at the end we make sure that everything gets its turn, so to speak.
Note that putting them at the end using naive list concatenation means that this algorithm will technically take linear time in how far it's got through the list to produce the next number. This can be handled by using a more efficient data structure such as Data.Sequence.
Upvotes: 7
Reputation: 12670
My Haskell sucks so here's an outline with fake code.
You can do it with a filter.
Note that for any odd number: 2x+1
You'll add 2x+6 to your list
For ever even number: 2x
You'll add 2x+3 and 2x+7
so:
filter (f) naturalNumbers
where f(x) :
true if (x-7) % 2 == 0
true if (x-6) % 2 == 0
true if (x-3) % 2 == 0
false otherwise
Upvotes: 0