Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

answered 2011-02-26 07:57:28 -0500

DSM gravatar image

You can also use a generator expression instead of a list comprehension; it's a very similar syntax, but whereas a list comprehension constructs an object of type list immediately, the generator expression is a lazy object:

sage: [p for p in (0..100) if is_prime(p) and (p%6)==1]
[7, 13, 19, 31, 37, 43, 61, 67, 73, 79, 97]
sage: 
sage: # generator expression
sage: (p for p in (0..100) if is_prime(p) and (p%6)==1)
<generator object <genexpr> at 0x10e0be3c0>

which you can turn into a list or a set or anything else:

sage: list(p for p in (0..100) if is_prime(p) and (p%6)==1)
[7, 13, 19, 31, 37, 43, 61, 67, 73, 79, 97]
sage: tuple(p for p in (0..100) if is_prime(p) and (p%6)==1)
(7, 13, 19, 31, 37, 43, 61, 67, 73, 79, 97)
sage: set(p for p in (0..100) if is_prime(p) and (p%6)==1)
set([97, 67, 37, 7, 73, 43, 13, 79, 19, 61, 31])

[Except for a technicality about the variables, list(p for p in (0..100)) is the same as [p for p in (0..100)].]

Why is this useful? Well, since you don't have to construct the list in memory, you can use it in ways that list comprehensions can't be used. E.g.

sage: time min([k for k in IntegerRange(10**5) if is_prime(1000*k+1)])
CPU times: user 2.69 s, sys: 0.12 s, total: 2.81 s
Wall time: 2.99 s
3
sage: time next(k for k in IntegerRange(10**5) if is_prime(1000*k+1))
CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
Wall time: 0.00 s
3

The listcomp is really slow because it has to construct the whole list first, before anything can happen with it, even though we're only going to care about a few terms. The genexp builds as you iterate.

FWIW, I'd have written your original function -- assuming you really wanted a list, and not a set -- as

sage: list(p for p in primes(100) if p % 6 == 1)
[7, 13, 19, 31, 37, 43, 61, 67, 73, 79, 97]

You can also use a generator expression expression instead of a list comprehension; it's a very similar syntax, but whereas a list comprehension constructs an object of type list immediately, the generator expression is a lazy object:

sage: [p for p in (0..100) if is_prime(p) and (p%6)==1]
[7, 13, 19, 31, 37, 43, 61, 67, 73, 79, 97]
sage: 
sage: # generator expression
sage: (p for p in (0..100) if is_prime(p) and (p%6)==1)
<generator object <genexpr> at 0x10e0be3c0>

which you can turn into a list or a set or anything else:

sage: list(p for p in (0..100) if is_prime(p) and (p%6)==1)
[7, 13, 19, 31, 37, 43, 61, 67, 73, 79, 97]
sage: tuple(p for p in (0..100) if is_prime(p) and (p%6)==1)
(7, 13, 19, 31, 37, 43, 61, 67, 73, 79, 97)
sage: set(p for p in (0..100) if is_prime(p) and (p%6)==1)
set([97, 67, 37, 7, 73, 43, 13, 79, 19, 61, 31])

[Except for a technicality about the variables, list(p for p in (0..100)) is the same as [p for p in (0..100)].]

Why is this useful? Well, since you don't have to construct the list in memory, you can use it in ways that list comprehensions can't be used. E.g.

sage: time min([k for k in IntegerRange(10**5) if is_prime(1000*k+1)])
CPU times: user 2.69 s, sys: 0.12 s, total: 2.81 s
Wall time: 2.99 s
3
sage: time next(k for k in IntegerRange(10**5) if is_prime(1000*k+1))
CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
Wall time: 0.00 s
3

The listcomp is really slow because it has to construct the whole list first, taking time and memory, before anything can happen with it, even though we're only going to care about a few terms. The genexp builds as you iterate.

FWIW, I'd have written your original function -- assuming you really wanted a list, and not a set -- as

sage: list(p for p in primes(100) if p % 6 == 1)
[7, 13, 19, 31, 37, 43, 61, 67, 73, 79, 97]