# List creation with local variables

Can one create a list with some assumptions inside [ ], without having to make loops?

List creation with local variables

Can one create a list with some assumptions inside [ ], without having to make loops?

1

Your question is whether you can create local variables inside an expression (in particular, inside a list comprehension): Is there a Python equivalent of the Haskell 'let'? The answer is basically no, unless you acknowledge abominations like this:

```
w=[n for n in prime_range(5,10000) if (lambda m: (lambda k: (n+m+k).is_prime())(previous_prime(m)))(previous_prime(n))]
```

Let me turn your original list comprehension into a function:

```
def my_primes0(lower, upper):
return [n for n in prime_range(lower,upper) if (n+previous_prime(n)+previous_prime(previous_prime(n))).is_prime()]
```

You create a list of primes in a range with `prime_range`

, but subsequently you use `previous_prime`

which does new superfluous primality tests. You can make better use of the list returned by`prime_range`

like so:

```
def my_primes1(lower, upper):
p = prime_range(lower, upper)
for i in range(len(p)-2):
if is_prime(p[i] + p[i+1] + p[i+2]):
yield p[i+2]
```

This is a generator which you can use to create a list as follows:

```
sage: w1 = list(my_primes1(5, 10000))
sage: w1 == w
True
```

These are the timings on my machine (I increased the upper bound to get into the millisecond range):

```
sage: timeit('my_primes0(5,10000)', number=125, repeat=3)
125 loops, best of 3: 35.8 ms per loop
sage: timeit('list(my_primes1(5, 10000))', number=125, repeat=3)
125 loops, best of 3: 6.16 ms per loop
```

In general, if you want to use local variables in the definition of an iterator then you should probably define a generator (using `yield`

).

1

Something like that has very recently been added to Python 3, in the form of "expression assignment" :=, but that would only help you once sage has migrated to Python 3. There is a workaround by using nested "for" clauses in list or iterator comprehensions:

```
w=[n for n in prime_range(5,100) for m in [pp(n)] for k in [pp(m)] if (n+m+k).is_prime() ];
```

I would not recommend it in this case because using "previous_prime" is a very expensive way of getting the previous prime if you're enumerating them in order anyway!

Given that prime_range returns a list anyway, there's no problem using it as such. If it were an iterator that lazily produces the values, there'd be a benefit in processing the generator to produce a sliding window:

```
from collections import deque
def iter_window(iterable,n):
T=iter(iterable)
result=deque((next(T) for i in range(n)),n)
yield tuple(result)
for t in T:
# the deque has max length n, so appending an
# element automatically drops the leftmost entry.
result.append(t)
yield tuple(result)
```

so that you can do

```
w=[n for k,m,n in iter_window(prime_range(2,100),3) if (n+m+k).is_prime()]
```

Asked: **
2019-01-02 06:33:53 -0600
**

Seen: **164 times**

Last updated: **Jan 02 '19**

Copyright Sage, 2010. Some rights reserved under creative commons license. Content on this site is licensed under a Creative Commons Attribution Share Alike 3.0 license.

For example in place of

pp=previous_prime;

w=[n for n in prime_range(5,100) if (n+pp(n)+pp(pp(n))).is_prime()];

I wold like to write something like this:

w=[n for n in prime_range(5,100) if (n+m+k).is_prime() "where" m=pp(n) and k=pp(m)];

Is that possible in some way?

I tried this

%timeit

pp=previous_prime;

w=[n for n in prime_range(5,1000) for m in prime_range(n//2,n) for k in prime_range(m//2,m) if m==pp(n) and k==pp(m) and (n+m+k).is_prime()];

(5 loops, best of 3: 861 ms per loop)

It works, but it is way slower than the original here:

%timeit

pp=previous_prime;

w=[n for n in prime_range(5,1000) if (n+pp(n)+pp(pp(n))).is_prime()];

(125 loops, best of 3: 1.61 ms per loop)