Daniel said:
Hello NG,
I am wondering if there were proposals or previous disscussions in this
NG considering using 'while' in comprehension lists
# pseudo code
i=2
lst=[i**=2 while i<1000]
You are actually describing two features that list comps don't natively support
- while-based termination, and calculating based on prior values of output. Of
course there are work-arounds for both, which others have shown. Here's another
approach:
The while-based termination can be easily achieved using itertools.takewhile, e.g.,:
>>> list(itertools.takewhile(lambda x: x < 10, range(100))) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>
the harder piece is to access the prior value. One way is like this:
def chasetail(start, func):
from itertools import tee
def mygen():
yield start
for i in (func(i) for i in iterators[0]):
yield i
iterators = tee(mygen())
return iterators[1]
the trick is to create two independent iterators, using itertools.tee, one of
which is consumed internally in the func(i) for i in iterators[0] generator
expression, the other is returned to use code.
Then you can combine these two approaches to get something semantically like
what you wanted in the first place (although not as pretty ;-)
>>> list(itertools.takewhile(lambda x: x < 1000, chasetail(2, lambda x: x*x))) [2, 4, 16, 256]
>>>
If you like this sort of thing, you might want to generalize the concept with a
Stream class. Here's minimal implementation:
import itertools as it
class Stream(object):
"""An extendable stream, that provides a separate iterator
(using itertools.tee) on every iteration request"""
def __init__(self, *iterables):
self.queue = list(iterables)
self.itertee = it.tee(self._chain(self.queue))[0]
def _chain(self, queue):
while queue:
for i in self.queue.pop(0):
self.head = i
yield i
def extend(self,other):
self.queue.append(other)
def __iter__(self):
"""Normal iteration over the iterables in self.queue in turn"""
return self.itertee.__copy__()
then, you can write your squaring algorithm as:
>>> s= Stream([2])
>>> s.extend(it.takewhile(lambda x: x < 1000, (i**2 for i in s)))
>>> list(s)
[2, 4, 16, 256]
Michael