Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > Re: while expression feature proposal

Reply
Thread Tools

Re: while expression feature proposal

 
 
Ian Kelly
Guest
Posts: n/a
 
      10-24-2012
On Wed, Oct 24, 2012 at 3:54 PM, Tim Chase
<(E-Mail Removed)> wrote:
> It may be idiomatic, but that doesn't stop it from being pretty
> ugly. I must say I really like the parity of Dan's
>
> while EXPR as VAR:
> BLOCK
>
> proposal with the "with" statement. It also doesn't fall prey to
> the "mistaken-assignment vs. intentional-assignment" found in most
> C-like languages. I could see a pretty reasonable PEP coming from this.


Often though the while test is not a simple boolean test of VAR. For example:

j = int(random() * n)
while j in selected:
j = int(random() * n)

It also doesn't flow quite as naturally. "with x as y" is
grammatically correct English. "while x as y" is not, and I wonder
how easily it might be confused for "while x is y", which is valid
Python and means something completely different.
 
Reply With Quote
 
 
 
 
Paul Rubin
Guest
Posts: n/a
 
      10-24-2012
Ian Kelly <(E-Mail Removed)> writes:
> j = int(random() * n)
> while j in selected:
> j = int(random() * n)


from itertools import dropwhile

j = dropwhile(lambda j: j in selected,
iter(lambda: int(random() * n), object()))
.next()

kind of ugly, makes me wish for a few more itertools primitives, but I
think it expresses reasonably directly what you are trying to do.
 
Reply With Quote
 
 
 
 
Ian Kelly
Guest
Posts: n/a
 
      10-24-2012
On Wed, Oct 24, 2012 at 5:08 PM, Paul Rubin <(E-Mail Removed)> wrote:
> from itertools import dropwhile
>
> j = dropwhile(lambda j: j in selected,
> iter(lambda: int(random() * n), object()))
> .next()
>
> kind of ugly, makes me wish for a few more itertools primitives, but I
> think it expresses reasonably directly what you are trying to do.


Nice, although a bit opaque. I think I prefer it as a generator expression:

j = next(j for j in iter(partial(randrange, n), None) if j not in selected)
 
Reply With Quote
 
Thomas Rachel
Guest
Posts: n/a
 
      10-25-2012
Am 25.10.2012 01:39 schrieb Ian Kelly:
> On Wed, Oct 24, 2012 at 5:08 PM, Paul Rubin <(E-Mail Removed)> wrote:
>> from itertools import dropwhile
>>
>> j = dropwhile(lambda j: j in selected,
>> iter(lambda: int(random() * n), object()))
>> .next()
>>
>> kind of ugly, makes me wish for a few more itertools primitives, but I
>> think it expresses reasonably directly what you are trying to do.

>
> Nice, although a bit opaque. I think I prefer it as a generator expression:
>
> j = next(j for j in iter(partial(randrange, n), None) if j not in selected)


This generator never ends. If it meets a non-matching value, it just
skips it and goes on.

The dropwhile expression, however, stops as soon as the value is found.

I think

# iterate ad inf., because partial never returns None:
i1 = iter(partial(randrange, n), None)
# take the next value, make it None for breaking:
i2 = (j if j in selected else None for j in i1)
# and now, break on None:
i3 = iter(lambda: next(i2), None)

would do the job.


Thomas
 
Reply With Quote
 
Paul Rudin
Guest
Posts: n/a
 
      10-25-2012
Paul Rubin <(E-Mail Removed)> writes:

> kind of ugly, makes me wish for a few more itertools primitives


JOOI, do you have specific primitives in mind?
 
Reply With Quote
 
Thomas Rachel
Guest
Posts: n/a
 
      10-25-2012
Am 25.10.2012 09:21 schrieb Thomas Rachel:

> I think
>
> # iterate ad inf., because partial never returns None:
> i1 = iter(partial(randrange, n), None)
> # take the next value, make it None for breaking:
> i2 = (j if j in selected else None for j in i1)
> # and now, break on None:
> i3 = iter(lambda: next(i2), None)
>
> would do the job.


But, as I read it now again, it might be cleaner to create an own
generator function, such as

def rand_values(randrange, n, selected):
# maybe: selected = set(selected) for the "not in"
while True:
val = partial(randrange, n)
if val not in selected: break
yield val

for value in rand_values(...):

or, for the general case proposed some posings ago:

def while_values(func, *a, **k):
while True:
val = func(*a, **k):
if not val: break
yield val

Thomas
 
Reply With Quote
 
Ian Kelly
Guest
Posts: n/a
 
      10-25-2012
On Thu, Oct 25, 2012 at 1:21 AM, Thomas Rachel
<(E-Mail Removed)>
wrote:
>> j = next(j for j in iter(partial(randrange, n), None) if j not in
>> selected)

>
>
> This generator never ends. If it meets a non-matching value, it just skips
> it and goes on.


next() only returns one value. After it is returned, the generator is
discarded, whether it has ended or not. If there were no valid values
for randrange to select, then it would descend into an infinite loop.
But then, so would the dropwhile and the original while loop.
 
Reply With Quote
 
Ian Kelly
Guest
Posts: n/a
 
      10-25-2012
On Thu, Oct 25, 2012 at 10:36 AM, Ian Kelly <(E-Mail Removed)> wrote:
> On Thu, Oct 25, 2012 at 1:21 AM, Thomas Rachel
> <(E-Mail Removed)>
> wrote:
>>> j = next(j for j in iter(partial(randrange, n), None) if j not in
>>> selected)

>>
>>
>> This generator never ends. If it meets a non-matching value, it just skips
>> it and goes on.

>
> next() only returns one value. After it is returned, the generator is
> discarded, whether it has ended or not. If there were no valid values
> for randrange to select, then it would descend into an infinite loop.
> But then, so would the dropwhile and the original while loop.


To demonstrate that the code does in fact return:

>>> selected = set(range(5))
>>> n = 10
>>> from functools import partial
>>> from random import randrange
>>> j = next(j for j in iter(partial(randrange, n), None) if j not in selected)
>>> j

5
 
Reply With Quote
 
Thomas Rachel
Guest
Posts: n/a
 
      10-25-2012
Am 25.10.2012 18:36 schrieb Ian Kelly:
> On Thu, Oct 25, 2012 at 1:21 AM, Thomas Rachel
> <(E-Mail Removed)>
> wrote:
>>> j = next(j for j in iter(partial(randrange, n), None) if j not in
>>> selected)

>>
>>
>> This generator never ends. If it meets a non-matching value, it just skips
>> it and goes on.

>
> next() only returns one value. After it is returned, the generator is
> discarded, whether it has ended or not. If there were no valid values
> for randrange to select, then it would descend into an infinite loop.
> But then, so would the dropwhile and the original while loop.


You are completely right. My solution was right as well, but didn't
match the problem...

Yours does indeed return one random value which is guaranteed not to be
in selected.

Mine returns random values until the value is not in selected. I just
misread the intention behind the while loop...


Thomas
 
Reply With Quote
 
Dan Loewenherz
Guest
Posts: n/a
 
      10-26-2012
It seems the topic of this thread has changed drastically from the originalmessage.

1) "while EXPR as VAR" in no way says that EXPR must be a boolean value. Infact, a use case I've run into commonly in web development is popping froma redis set. E.g.

client = StrictRedis()
while True:
profile_id = client.spop("profile_ids")
if not profile_id:
break
print profile_id

In this case, profile_id is "None" when the loop breaks. It would be much more straightforward (and more Pythonic, IMO), to write:

client = StrictRedis()
while client.spop("profile_ids") as profile_id:
print profile_id

2) Although not originally intended, I kind of like the "if" statement change proposed later in this thread. It certainly makes sense, since both while and if are "conditional" statements that are commonly followed by an assignment (or vice versa).

3) I don't think the use case I brought up is solved nicely by wrapping a function / lambda in a generator and using a for loop. E.g.

def helper(f):
value = f()
if value:
yield value

for profile_id in helper(lambda: client.spop("profile_ids")):
print profile_id

This works too, I guess

def helper(f, *args, **kwargs):
value = f(*args, **kwargs)
if value:
yield value

for profile_id in helper(client.spop, "profile_ids"):
print profile_id

Either way, it adds too much mental overhead. Every developer on a project has to now insert x lines of code before a for loop or import a helper method from some module, and do this every time this pattern reappears. It's not something I would want to do in one of my projects, since it makes thingsharder to understand. So all in all, it's a net negative from just doing things the canonical way (with the while / assignment pattern).

Dan
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: while expression feature proposal Cameron Simpson Python 6 10-25-2012 04:15 PM
Re: while expression feature proposal Chris Angelico Python 0 10-24-2012 10:40 PM
while expression feature proposal Dan Loewenherz Python 1 10-24-2012 10:19 PM
Re: while expression feature proposal Tim Chase Python 0 10-24-2012 09:54 PM
Re: while expression feature proposal Ian Kelly Python 0 10-24-2012 09:34 PM



Advertisments