Using ‘while’ loops in a list comprehension
The fundamental problem here is that initializing an integer and manually incrementing it within a while loop is the wrong way to iterate over a sequence of numbers in Python. Your first code block would more appropriately be x = []; for i in range(5, 21, 10): x.append(i) , which has a much clearer path for conversion into a comprehension.
3 Answers 3
You don’t need a list comprehension for that. range will just do:
A while loop is not possible inside of a list comprehension. Instead, you could do something like this:
def your_while_generator(): i = 5 while i
No, you cannot use while in a list comprehension.
From the grammar specification of Python, only the following atomic expressions are allowed:
atom: ('(' [yield_expr|testlist_comp] ')' | '[' [testlist_comp] ']' | '' | NAME | NUMBER | STRING+ | '. ' | 'None' | 'True' | 'False')
The expression corresponding to a list comprehension - testlist_comp looks like the following in Python 3:
testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
Here, the only statements allowed are
test: or_test ['if' or_test 'else' test] | lambdef star_expr: '*' expr comp_for: [ASYNC] 'for' exprlist 'in' or_test [comp_iter]
comp_if: 'if' test_nocond [comp_iter] comp_iter: comp_for | comp_if
There is not a single while statement allowed anywhere. The only keywords you are allowed to use is a for , for a for loop.
Solution
Use a for loop, or take advantage of itertools .
List comprehension using while loop?
Is there a way to use a while loop in a list comprehension. For example, I have a single line Fibonacci generator:
[int(((1+(5**0.5))**n-(1-(5**0.5))**n)/(2**n*(5**0.5))) for n in range(100)]
but I'd like it to stop at a certain outcome, rather than just run a certain number of times. (i.e. all Fibonacci sequence numbers below 4,000,000) This is a question about list-comprehension, not about lists in general. The more generic phrasing might be this:
[(formula using incrementing variable) for incrementing variable while (result is less than specified amount)]
You can use a generator expression instead of a list comprehension, and then loop over it until some point, or use itertools.takewhile() .
That said, using the explicit formula for the Fibonacci numbers is completely pointless if you want to generate them one by one anyway. Just use the recursion relation in a while loop.
The point isn't to complete the desired exercise, but rather to ask if there is a way to nest the while loop into the list comprehension. I get that you can do this with a lambda, I'm just wondering if a while statement can be placed in this phrasing. I've tried it a couple of different ways and keep getting syntax errors based on the while.
1 Answer 1
python don't have such feature of using while in a comprehension (which is like a map combined with filter), but you can accomplished that using other tools like making a function that do what you desire or using your best friend the itertools module. For example
>>> from itertools import takewhile >>> def fib(): fk,fk1 = 0,1 while True: yield fk fk, fk1 = fk1, fk + fk1 >>> list(takewhile(lambda fn:fn<100,fib())) [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] >>>
example 2, with a function
>>> def fib_while(tope): fk,fk1 = 0,1 while fk < tope: yield fk fk,fk1 = fk1, fk + fk1 >>> list(fib_while(100)) [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] >>>
oh, I forgot to mention, but your formula for getting the Fibonacci numbers, even if mathematical correct, is doom to fail to get the real value for a large enough n, because floating point arithmetic rounding errors
the point of divergence is very easy to found (using the above fib )
>>> def fib_float(n): return int(((1+(5**0.5))**n-(1-(5**0.5))**n)/(2**n*(5**0.5))) >>> [n for n,f in zip(range(100),fib()) if f!=fib_float(n)] ) [72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99] >>>
so for all n>=72 what you get are not Fibonacci numbers.
if you only care for all numbers in the sequence below 4,000,000 then that is not a problem of course as the limit is n=33
'while' in list comprehension?
wouldn't it be useful to have a 'while' conditional in addition to
'if' in list comprehensions?
foo = []
for i in bar:
if len(i) == 0:
break
foo.append(i)
foo = [ i for i in bar while len(i)>0 ]
Is there any reason for not having this kind of thing? I actually
miss it pretty often.
foo = [ i for i in bar while len(i)>0 ]
Is there any reason for not having this kind of thing? I actually
miss it pretty often.
How is this different from:
foo = [ i for i in bar if len(i) ]
Emile> How is this different from:
Emile> foo = [ i for i in bar if len(i) ]
_ = []
for i in bar:
if not (len(i) > 0):
break
_.append(i)
return _
_ = []
for i in bar:
if len(i) > 0:
_.append(i)
return _
Use 2.3's itertools:
foo = [i for i in itertools.takewhile(lambda i: len(i) > 0, bar)]
or maybe
foo = list(itertools.takewhile(len, bar)]
or even
foo = itertools.takewhile(len, bar) # an iterable, not a list
foo = [ i for i in bar while len(i)>0 ]
Is there any reason for not having this kind of thing? I actually
miss it pretty often.
How is this different from:
foo = [ i for i in bar if len(i) ]
My reading is that the comprehension would stop at the first i whose len
were 0. e.g.
foo = []
for i in bar:
if len(i) == 0:break
foo.append(i)
Bob Gailer
bg*****@alum.rpi.edu
303 442 2625
---
Outgoing mail is certified Virus Free.
Checked by AVG anti-virus system (http://www.grisoft.com).
Version: 6.0.525 / Virus Database: 322 - Release Date: 10/9/2003
foo = [ i for i in bar while len(i)>0 ]
Is there any reason for not having this kind of thing? I actually
miss it pretty often.
How is this different from:
foo = [ i for i in bar if len(i) ]
The idea is that 'while' stops iterating through the list when the condition
isn't met. 'if' just doesn't output anything. e.g.:
bar = ['this', 'is', 'a', 'list', '', 'see?']
[i for i in bar if len(i)] ['this', 'is', 'a', 'list', 'see?'] [i for i in bar while len(i)] # Pretend
What's your typical use for this?
I guess I see nothing really wrong with it, although I thought list
comprehensions were supposed to make the iteration transparent--'while'
kinda destroys the illusion. But these are the only two looping constructs
that make sense in a list comprehension, so why not support both? OTOH,
'while' makes no sense in a dictionary comprehension (and presumably we'll
have those one day.)
Is there any reason for not having this kind of thing? I actually
miss it pretty often.
Could you provide an example where you need it?
From your message, it seems "bar" might be a list of strings. If so, you could use:
wouldn't it be useful to have a 'while' conditional in addition to
'if' in list comprehensions?
foo = []
for i in bar:
if len(i) == 0:
break
foo.append(i)
foo = [ i for i in bar while len(i)>0 ]
while is simply not same as if: break!
if executes once for each value of i, while indefinitely.
If you translate back by current rule, which will not change, you get:
foo = []
for i in bar:
while len(i) >0:
foo.append(i)
while is simply not same as if: break!
if executes once for each value of i, while indefinitely.
If you translate back by current rule, which will not change, you get:
'while' is an English word which has meaning independant of the
existing Python 'while' loop. It is not necessarily wrong to apply a
different aspect of that meaning in a list comprehension.
Besides, I read the syntax as equating to.
foo = []
for i in bar while len(i) >0:
foo.append(i)
Yes, I know that isn't a legal Python loop. My point is that it didn't
look like a nested loop to me, but rather like an additional qualifier
on the existing loop.
That said, you do have a point - multiple 'for' parts in a list
comprehension act as nested loops, so maybe a while part should too.
The trouble is that a standalone while loop probably makes little
sense in a list comprehension - sure you have a place to put the loop
condition, but what about the initialisation and body?
If there were a real while-part in a list comprehension, it would
probably need those things to become explicit (becoming a lot like the
C for loop) - something like.
Nah - damn silly idea.
--
Steve Horne
steve at ninereeds dot fsnet dot co dot uk
Using 'while' loops in a list comprehension
Is there a way to convert it to a list comprehension like this?
cashmoney11
People also ask
Because of differences in how Python implements for loops and list comprehension, list comprehensions are almost always faster than for loops when performing operations.
Sure you can use a while loop, every for loop can be written as a while loop if you really have to for some reson but I don't see any advantages over your for loop.
List comprehensions are faster than for loops to create lists. But, this is because we are creating a list by appending new elements to it at each iteration.
List comprehensions are great because they require less lines of code, are easier to comprehend, and are generally faster than a for loop.
2 Answers
You don't need a list comprehension for that. range will just do:
A while loop is not possible inside of a list comprehension. Instead, you could do something like this:
def your_while_generator(): i = 5 while i
Francisco
No, you cannot use while in a list comprehension.
From the grammar specification of Python, only the following atomic expressions are allowed:
atom: ('(' [yield_expr|testlist_comp] ')' | '[' [testlist_comp] ']' | '' | NAME | NUMBER | STRING+ | '. ' | 'None' | 'True' | 'False')
The expression corresponding to a list comprehension - testlist_comp looks like the following in Python 3:
testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
Here, the only statements allowed are
test: or_test ['if' or_test 'else' test] | lambdef star_expr: '*' expr comp_for: [ASYNC] 'for' exprlist 'in' or_test [comp_iter]
comp_if: 'if' test_nocond [comp_iter] comp_iter: comp_for | comp_if
There is not a single while statement allowed anywhere. The only keywords you are allowed to use is a for , for a for loop.
Solution
Use a for loop, or take advantage of itertools .
answered Sep 21 '22 23:09