Variations on a loop

There are several different blocks of code you could tack onto a loop (I've deliberately chosen somewhat unusual words to express these here): for x in items: # body interstitially: # things to do between loop iteration # (executed after each iteration in the loop when there is a next value) subsequently: # things to do after the last element of the loop is processed # (when the loop is not exited by break) contrariwise: # things to do if the list was empty For example: result = "" for x in items: result += str(x) interstitially: result += ", " contrariwise: result = "no data" When I first learned that Python had an 'else' clause on loops, I assumed it meant 'contrariwise'. I was surprised that it actually meant 'subsequently'. To be more clear, contrariwise is essentially equivalent to: empty = True for x in items: empty = False # body if empty: # do contrariwise code and interstitially is essentially equivalent to: first = True for x2 in items: if not first: # do interstitial code first = False x = x2 # body I think these are common/useful paradigms. I'm curious what others think. --- Bruce

On Wed, Aug 27, 2008 at 10:07 PM, Bruce Leban <bruce@leapyear.org> wrote:
Just for comparison, (assuming my Perl6 book is still accurate), "interstitially" is equivalent to "NEXT" blocks in Perl6 and "continue" blocks in Perl5. I don't think that's necessarily a good thing, but anyway... - Chris ======== Follow the path of the Iguana... Rebertia: http://rebertia.com Blog: http://blog.rebertia.com

Bruce Leban wrote:
We already have statements that only apply within loops (break and continue), how about some expressions that only apply in for loops: 'last' and 'empty': for x in items: result += str(x) if not last: result += ', ' else: if empty: result = "no data" -bruce (not to be confused with Bruce :-)

Bruce Frederiksen wrote:
I don't know. I don't like either. What about named loops/loop objects? Using this you have no need for new keywords: myloop = for x in items: result += str(x) if not myloop.last: result += ', ' if myloop.empty: result = 'no data' However, how to calculate the last property for generators where it might depend on some side effects if it's really the last item? Other things would be possible, too: a = for x in xs: b = for y in ys: c = for z in zs: if cond(z): break b if cond(y): continue a However, this would increase clutter and might be a bad idea for the same reason goto is a bad idea. I also would be possible to pass the loop object to another function which then might call continue/break which changes the controlflow completely and makes the code very hard to read. So I guess this is a bad idea. -panzi

On Fri, Aug 29, 2008 at 6:19 AM, Mathias Panzenböck < grosser.meister.morti@gmx.net> wrote:
This is interesting. I don't like the fact that you have the loop variable scope extending past the end of the loop and it's not clear what we can do with that. It occurs to me that we can do something like this without changes to the language but it's clumsy: for x in funky(items): result += str(x.value) if not x.last(): result += ', ' else: if x == funky_empty: result = 'no data'
Note that I wrote x.last() not x.last: when you check last, it pre-fetches the next value. That's not an issue in the above code but could be in other cases, so you have to be careful when you use it. This could also provide other things like automatic counting of loop iterations: for x in funky(items): foo(x.index, x.value) It wouldn't provide the ability to do nested breaks and continues which I'd like to see. --- Bruce

Bruce Leban wrote:
So x.first seems to make more sense than x.last. You could also name the loops like: outer = funky(items1) for x in outer: middle = funky(items2) for y in middle: inner = funky(items3) for z in inner: middle.continue_() It seems to implement this, funky.continue_ and funky.break_ would have to raise exceptions. If the for loop honored the 'throw' method (of generators), it seems like this might be made to work... -bruce

This idea elif x == funky_empty: result = 'no data' Can be accomplished with if not len(items): result = 'no data' which means that part of the proposal can be scratched IMHO.

On Fri, Aug 29, 2008 at 6:25 PM, Bruce Leban <bruce@leapyear.org> wrote:
If len(items) requires iterating across the entire list, then you're not using a Python list object, you're using someone else's (poorly implemented) list data structure. Python lists give len(obj) in O(1). Steve -- I'm not *in*-sane. Indeed, I am so far *out* of sane that you appear a tiny blip on the distant coast of sanity. --- Bucky Katt, Get Fuzzy

On Fri, Aug 29, 2008 at 7:17 PM, Steven Bethard <steven.bethard@gmail.com>wrote:
items need not be a literal list. If you can compute this len([t for t in range(100) if foo(t)]): without iterating over the list, then there's a Turing award waiting for you. :-) (Yes, if you know what foo is perhaps you can optimize that. I'm not counting that.) This is also why you may need an intermediate variable: you don't want to compute that list more than once because foo(t) may have a side effect. --- Bruce

On Sat, Aug 30, 2008 at 12:49 AM, Bruce Leban <bruce@leapyear.org> wrote:
See what you said above: "len(items) requires iterating across the list". The list comprehension is iterating over the items, not the call to len(). So I *can* compute "len(items)" without iterating over the list. Steve -- I'm not *in*-sane. Indeed, I am so far *out* of sane that you appear a tiny blip on the distant coast of sanity. --- Bucky Katt, Get Fuzzy

Bruce Leban schrieb:
This is moot anyway: in general, iterators have no __len__(). Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out.

On 29 Aug 2008, at 13:49, Bruce Frederiksen wrote:
note that this can be achieved without 'empty' for x in items: result += str(x) if last: break else: result += ', ' else: result = "no data" Something like this can be implemented in Python with the following helper function: def flaglast(iterable): it = iter(iterable) for prev in it: break else: return for item in it: yield prev, False prev = item yield prev, True E.g.
list(flaglast([1,2,3])) [(1, False), (2, False), (3, True)]
Then you can write your code snippet as: for x, last in flaglast(items): result += str(x) if last: break else: result += ', ' else: result = "no data" -- Arnaud

On 30 Aug 2008, at 16:38, Arnaud Delobelle wrote:
Also having a 'last' local to a loop implies a lot of overhead in case one is looping over an iterable whose length is not known - in essence the overhead in my flaglast function. Finally, my function works gracefully with nested loops: for i, i_last in flaglast(items): for j, j_last in flaglast(jtems): ... without the need for a 'named loop', about which I feel very circumspect. -- Arnaud

On Sat, Aug 30, 2008 at 10:56 AM, Arnaud Delobelle <arnodel@googlemail.com>wrote:
Unfortunately, your function has to get the N+1st item before the body processes the Nth element so if getting the elements has side effects and the body might break, then this changes the behavior of the loop. With an explicit call to an <is this the last> function, we know exactly when it will peek ahead in the list. --- Bruce

On Wed, Aug 27, 2008 at 10:07 PM, Bruce Leban <bruce@leapyear.org> wrote:
Just for comparison, (assuming my Perl6 book is still accurate), "interstitially" is equivalent to "NEXT" blocks in Perl6 and "continue" blocks in Perl5. I don't think that's necessarily a good thing, but anyway... - Chris ======== Follow the path of the Iguana... Rebertia: http://rebertia.com Blog: http://blog.rebertia.com

Bruce Leban wrote:
We already have statements that only apply within loops (break and continue), how about some expressions that only apply in for loops: 'last' and 'empty': for x in items: result += str(x) if not last: result += ', ' else: if empty: result = "no data" -bruce (not to be confused with Bruce :-)

Bruce Frederiksen wrote:
I don't know. I don't like either. What about named loops/loop objects? Using this you have no need for new keywords: myloop = for x in items: result += str(x) if not myloop.last: result += ', ' if myloop.empty: result = 'no data' However, how to calculate the last property for generators where it might depend on some side effects if it's really the last item? Other things would be possible, too: a = for x in xs: b = for y in ys: c = for z in zs: if cond(z): break b if cond(y): continue a However, this would increase clutter and might be a bad idea for the same reason goto is a bad idea. I also would be possible to pass the loop object to another function which then might call continue/break which changes the controlflow completely and makes the code very hard to read. So I guess this is a bad idea. -panzi

On Fri, Aug 29, 2008 at 6:19 AM, Mathias Panzenböck < grosser.meister.morti@gmx.net> wrote:
This is interesting. I don't like the fact that you have the loop variable scope extending past the end of the loop and it's not clear what we can do with that. It occurs to me that we can do something like this without changes to the language but it's clumsy: for x in funky(items): result += str(x.value) if not x.last(): result += ', ' else: if x == funky_empty: result = 'no data'
Note that I wrote x.last() not x.last: when you check last, it pre-fetches the next value. That's not an issue in the above code but could be in other cases, so you have to be careful when you use it. This could also provide other things like automatic counting of loop iterations: for x in funky(items): foo(x.index, x.value) It wouldn't provide the ability to do nested breaks and continues which I'd like to see. --- Bruce

Bruce Leban wrote:
So x.first seems to make more sense than x.last. You could also name the loops like: outer = funky(items1) for x in outer: middle = funky(items2) for y in middle: inner = funky(items3) for z in inner: middle.continue_() It seems to implement this, funky.continue_ and funky.break_ would have to raise exceptions. If the for loop honored the 'throw' method (of generators), it seems like this might be made to work... -bruce

This idea elif x == funky_empty: result = 'no data' Can be accomplished with if not len(items): result = 'no data' which means that part of the proposal can be scratched IMHO.

On Fri, Aug 29, 2008 at 1:47 PM, David Blaschke <dwblas@gmail.com> wrote:
Not quite: len(items) requires iterating across the entire list which may not be necessary or desirable. Plus you may need an intermediate variable to store it. I think that's inferior. --- Bruce

On Fri, Aug 29, 2008 at 6:25 PM, Bruce Leban <bruce@leapyear.org> wrote:
If len(items) requires iterating across the entire list, then you're not using a Python list object, you're using someone else's (poorly implemented) list data structure. Python lists give len(obj) in O(1). Steve -- I'm not *in*-sane. Indeed, I am so far *out* of sane that you appear a tiny blip on the distant coast of sanity. --- Bucky Katt, Get Fuzzy

On Fri, Aug 29, 2008 at 7:17 PM, Steven Bethard <steven.bethard@gmail.com>wrote:
items need not be a literal list. If you can compute this len([t for t in range(100) if foo(t)]): without iterating over the list, then there's a Turing award waiting for you. :-) (Yes, if you know what foo is perhaps you can optimize that. I'm not counting that.) This is also why you may need an intermediate variable: you don't want to compute that list more than once because foo(t) may have a side effect. --- Bruce

On Sat, Aug 30, 2008 at 12:49 AM, Bruce Leban <bruce@leapyear.org> wrote:
See what you said above: "len(items) requires iterating across the list". The list comprehension is iterating over the items, not the call to len(). So I *can* compute "len(items)" without iterating over the list. Steve -- I'm not *in*-sane. Indeed, I am so far *out* of sane that you appear a tiny blip on the distant coast of sanity. --- Bucky Katt, Get Fuzzy

Bruce Leban schrieb:
This is moot anyway: in general, iterators have no __len__(). Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out.

On 29 Aug 2008, at 13:49, Bruce Frederiksen wrote:
note that this can be achieved without 'empty' for x in items: result += str(x) if last: break else: result += ', ' else: result = "no data" Something like this can be implemented in Python with the following helper function: def flaglast(iterable): it = iter(iterable) for prev in it: break else: return for item in it: yield prev, False prev = item yield prev, True E.g.
list(flaglast([1,2,3])) [(1, False), (2, False), (3, True)]
Then you can write your code snippet as: for x, last in flaglast(items): result += str(x) if last: break else: result += ', ' else: result = "no data" -- Arnaud

On 30 Aug 2008, at 16:38, Arnaud Delobelle wrote:
Also having a 'last' local to a loop implies a lot of overhead in case one is looping over an iterable whose length is not known - in essence the overhead in my flaglast function. Finally, my function works gracefully with nested loops: for i, i_last in flaglast(items): for j, j_last in flaglast(jtems): ... without the need for a 'named loop', about which I feel very circumspect. -- Arnaud

On Sat, Aug 30, 2008 at 10:56 AM, Arnaud Delobelle <arnodel@googlemail.com>wrote:
Unfortunately, your function has to get the N+1st item before the body processes the Nth element so if getting the elements has side effects and the body might break, then this changes the behavior of the loop. With an explicit call to an <is this the last> function, we know exactly when it will peek ahead in the list. --- Bruce
participants (8)
-
Arnaud Delobelle
-
Bruce Frederiksen
-
Bruce Leban
-
Chris Rebert
-
David Blaschke
-
Georg Brandl
-
Mathias Panzenböck
-
Steven Bethard