[Haskell-cafe] list comprehension doesn't work
Richard A. O'Keefe
ok at cs.otago.ac.nz
Wed May 15 02:20:33 CEST 2013
On 15/05/2013, at 2:57 AM, John wrote:
> Hi,
>
> I have to write a function which returns a list of all pairs (x,y) where x,
> y ∈ N AND:
> – x is the product of two natural numbers (x = a · b, where a, b ∈ N) AND
> – x is really bigger than 5 but really smaller than 500, AND
> – y is a squer number (y = c² where c ∈ N) NOT greater than 1000, AND
> – x is a divisor of y.
Step 1. If a >= 0 and b >= 0 & x = a*b & x < 500 & x > 5
then a > 0 and a < 500
and b > 0 and b < 500.
This suggests something like
xs = [x |
a <- [1..499],
b <- [1..499],
let x = a*b,
5 < x, x < 500
]
Step 2.
However, that will generate duplicates. If a*b = x then b*a = x, and
non-square values of x will be generated twice.
Let's just filter [6..499].
If x is in that range, and a is a number in the range [1..x-1],
and x `mod` a is zero, then x = a*b for some integer b, and b will
_have_ to be in the range you are looking for.
xs = [x |
x <- [6..499],
or [x `mod` a == 0 | a <- [1..x-1]]
]
xs has 494 elements. At first I was surprised to see that 7 was in
the list. However, if x = 7, a = 7, b = 1, then indeed a and b are
natural numbers and x is there product, so _every_ number in the
range 6..499 qualifies.
Step 3. If c >= 0 and y = c*c and y <= 1000
then c >= 0 and c <= 31
This gives us
ys = [c*c | c <- [0..31]]
or even
ys = map (^2) [0..31]
which of course has 32 elements.
Step 4.
Now all that's left to test is whether x divides some y:
[(x,y) | x <- xs, y <- ys, y `mod` x == 0
pairs :: [(Int,Int)]
pairs = [(x,y) | x <- xs, y <- ys, y `div` x == 0]
where xs = [x | x <- [6..499], or [x `mod` a == 0 | a <- [1..x-1]]]
ys = [c*c | c <- [0..31]]
This has 641 elements.
If the definition was supposed to be that x is a *composite* number
with factors a, b, then we need to search for "a" in the range [2..x1].
Using
pairs = [(x,y) | x <- xs, y <- ys, y `div` x == 0]
where xs = [x | x <- [6..499], or [x `mod` a == 0 | a <- [2..x-1]]]
ys = [c*c | c <- [0..31]] -- ^
xs has 402 elements and pairs has 546 elements.
> listPairs :: [(Int, Int)]
> listPairs = [(x,y) | x<-[0..], y<-[0..], x<-[0..]*[0..], x > 5, x < 500,
> (y*y) < 1001, mod y x == 0]
>
> However it doesn't work unfortunatly
>
> Could anyone tell me where my mistake is?
One mistake is that [0..]*[0..] is a type error.
* wants a pair of numbers, and [0..] is not a number.
(There are modules that make lists "numbers", but then
as*bs is usually the same as zipWith (*) as bs, which
is not what you want anyway.)
The main mistake is that
[x | x <- [0..], x > 5, x < 500]
is equivalent to
for (x = 0; true; x++) if (x > 5 && x < 500) yield x
That is, the [0..] part is happy to keep on generating
increasing integers, it neither knows, nor does it care,
that there is an x < 500 test downstream.
If you really want an infinite set searched, by all means
use [0..]. But if you only want a finite range, put BOTH
bounds in the interval so that it will stop.
More information about the Haskell-Cafe
mailing list