i=2; lst=[i**=2 while i<1000]
Michael Spencer
mahs at telcopartners.com
Tue Dec 6 14:32:57 EST 2005
Daniel Schüle wrote:
> 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.
>>> it = chasetail(2, lambda x: x*x) #careful - this won't terminate
>>> it.next()
2
>>> it.next()
4
>>> it.next()
16
>>> it.next()
256
>>> it.next()
65536
>>>
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
More information about the Python-list
mailing list