2

list comprehension haskell

 paar = [(a,b) | a<-[a | a<-[1..], mod a 3 == 0], b<-[b*b | b<-[1..]]]

a = divisor 3 b = square

The Elements must be constructed by equitable order.

the test >elem (9, 9801) must be True

my Error

Main> elem (9, 9801) test

ERROR - Garbage collection fails to reclaim sufficient space

How can I implement this with Cantor's diagonal argument?

thx

corium
  • 53
  • 4
  • 1
    An advice: install Haskell Platform, available here: http://hackage.haskell.org/platform/ – Tener May 10 '11 at 17:49
  • 2
    It's very unclear what the meaning of `paar` is. What do you want this list to look like? The elem function does indeed work on infinite lists (as long as the answer is `True`), but the way you generate the list is causing problems. – Dan Burton May 10 '11 at 18:22

2 Answers2

7

Not quite sure what your goal is here, but here's the reason why your code blows up.

Prelude> let paar = [(a,b) | a<-[a | a<-[1..], mod a 3 == 0], b<-[b*b | b<-[1..]]]
Prelude> take 10 paar
[(3,1),(3,4),(3,9),(3,16),(3,25),(3,36),(3,49),(3,64),(3,81),(3,100)]

Notice you're generating all the (3, ?) pairs before any other. The elem function works by searching this list linearly from the beginning. As there are an infinite number of (3, ?) pairs, you will never reach the (9, ?) ones.

In addition, your code is probably holding on to paar somewhere, preventing it from being garbage collected. This results in elem (9, 9801) paar taking not only infinite time but also infinite space, leading to the crash you described.

Ultimately, you probably need to take another approach to solving your problem. For example, something like this:

elemPaar :: (Integer, Integer) -> Bool
elemPaar (a, b) = mod a 3 == 0 && isSquare b
    where isSquare = ...

Or alternatively figure out some other search strategy than straight up linear search through an infinite list.

hammar
  • 138,522
  • 17
  • 304
  • 385
4

Here's an alternate ordering of the same list (by hammar's suggestion):

-- the integer points along the diagonals of slope -1 on the cartesian plane,
-- organized by x-intercept
-- diagonals = [ (0,0), (1,0), (0,1), (2,0), (1,1), (0,2), ...
diagonals = [ (n-i, i)  | n <- [0..], i <- [0..n] ]

-- the multiples of three paired with the squares
paar = [ (3*x, y^2) | (x,y) <- diagonals ]

and in action:

ghci> take 10 diagonals
[(0,0),(1,0),(0,1),(2,0),(1,1),(0,2),(3,0),(2,1),(1,2),(0,3)]
ghci> take 10 paar
[(0,0),(3,0),(0,1),(6,0),(3,1),(0,4),(9,0),(6,1),(3,4),(0,9)]
ghci> elem (9, 9801) paar
True

By using a diagonal path to iterate through all the possible values, we guarantee that we reach each finite point in finite time (though some points are still outside the bounds of memory).

As hammar points out in his comment, though, this isn't sufficient, as it will still take an infinite amount of time to get a False answer.

However, we have an order on the elements of paar, namely (3*a,b^2) comes before (3*c,d^2) when a + b < c + d. So to determine whether a given pair (x,y) is in paar, we only have to check pairs (p,q) while p/3 + sqrt q <= x/3 + sqrt y.

To avoid using Floating numbers, we can use a slightly looser condition, that p <= x || q <= y. Certainly p > x && q > y implies p/3 + sqrt q > x/3 + sqrt y, so this will still include any possible solutions, and it's guaranteed to terminate.

So we can build this check in

-- check only a finite number of elements so we can get a False result as well
isElem (p, q) = elem (p,q) $ takeWhile (\(a,b) -> a <= p || b <= q) paar

And use it:

ghci> isElem (9,9801)
True
ghci> isElem (9,9802)
False
ghci> isElem (10,9801)
False
rampion
  • 87,131
  • 49
  • 199
  • 315
  • Nice solution, but there's still an issue here. If you are searching for something that's _not_ in the list, you will keep going forever. You need a variant of `elem` that knows when it's safe to stop. – hammar May 10 '11 at 17:45