Search code examples
haskelllist-comprehensioninfinitehamming-numberssmooth-numbers

Haskell List comprehensions infinite list problem


I'm trying to learn Haskell and comprehension lists but cannot find solution on this:

mylist = [x*y | x <- [1..], y <- [1..]]

After my trials the result is something like this

mylist = [1,2,3,4,5,...]

because in list comprehensions, x takes the value 1,and then y changes value repeatedly.

But my goal is to achieve a different assignment so as to have the following result:

mylist = [1,2,2,4,3,3,6.....]

I mean i want the combinations being mixed and not each one apart,because I have a serious problem to have the suitable result.

I will give a more specific example.

I want a list that will have all numbers of this form:

num = 2^x * 3^y 

x and y must take all values >= 0.

My approach is the following:

powers = [2^x * 3^y | x <- [0..], y <- [0..]]

But in this way I only take powers of 3, because x is constantly 0.

I tried this one

multiples = nub (merge (<=) powers2 powers3)
powers3 = [2^x * 3^y | x <- [0..], y <- [0..]]
powers2 = [2^x * 3^y | y <- [0..], x <- [0..]]

so as to merge the different ones but again,the values 6,12,etc. are missing - the result is this:

mylist = [1,2,3,4,8,9,16,27,32,64,81...]

Solution

  • The code that you show,

    multiples = nub (merge (<=) powers2 powers3)
    powers3 = [2^x * 3^y | x <- [0..], y <- [0..]]
    powers2 = [2^x * 3^y | y <- [0..], x <- [0..]]
    

    is equivalent to

    powers3 = [2^x * 3^y | x <- [0], y <- [0..]]
            = [2^0 * 3^y | y <- [0..]]
            = [3^y | y <- [0..]]
    powers2 = [2^x * 3^y | y <- [0], x <- [0..]] 
            = [2^x * 3^0 | x <- [0..]]
            = [2^x | x <- [0..]]
    

    so you only produce the powers of 2 and 3, without any mixed multiples. As such, there are guaranteed to be no duplicates in the stream, and the nub was not necessary. And of course it's incomplete.

    But let's look at it at another angle. It was proposed in the comments to create a 2D grid out of these numbers:

    mults23_2D = [[2^x * 3^y | y <- [0..]] | x <- [0..]]
    {-
       1   3   9   27  81  ...
       2   6  18   54  ...
       4  12  36  108  ...
       8  24  72  ...
      16  ...
      .......     
    -}
    

    Now we're getting somewhere. At least now none are skipped. We just need to understand how to join them into one sorted, increasing stream of numbers. Simple concat of course won't do. We need to merge them in order. A well-known function merge does that, provided the arguments are already ordered, increasing lists.

    Each row produced is already in increasing order, but there are infinitely many of them. Never fear, foldr can do it. We define

    mults23 = foldr g [] [[2^x * 3^y | y <- [0..]] | x <- [0..]]
      -- foldr g [] [a,b,c,...] == a `g` (b `g` (c `g` (....)))
     where
     g (x:xs) ys = 
    

    Here it is a little bit tricky. If we define g = merge, we'll have a run-away recursion, because each merge will want to know the head element of its "right" (second) argument stream.

    To prevent that, we produce the leftmost element right away.

                    x : merge xs ys
    

    And that's that.