<html><head><meta http-equiv="Content-Type" content="text/html charset=iso-8859-1"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; " class=""><div class=""><font class="">Haven't read back far enough to know whether this is as interesting as it looks to me, but..</font></div><div class=""><font face="Consolas" class=""><br class=""></font></div><div class=""><div class=""><font face="Consolas" class="">>>> def until(items):</font></div><div class=""><font face="Consolas" class="">... stop = None</font></div><div class=""><font face="Consolas" class="">... counter = 0</font></div><div class=""><font face="Consolas" class="">... items = iter(items)</font></div><div class=""><font face="Consolas" class="">... while not stop:</font></div><div class=""><font face="Consolas" class="">... stop = yield next(items)</font></div><div class=""><font face="Consolas" class="">... if stop: </font></div><div class=""><font face="Consolas" class="">... yield</font></div><div class=""><font face="Consolas" class="">... counter += 1</font></div><div class=""><font face="Consolas" class="">... print(counter)</font></div><div class=""><font face="Consolas" class="">... </font></div><div class=""><font face="Consolas" class="">>>> gen = until(range(15))</font></div><div class=""><font face="Consolas" class="">>>> stop = lambda: gen.send(True)</font></div><div class=""><font face="Consolas" class="">>>> [x for x in gen if x < 3 or stop()]</font></div><div class=""><font face="Consolas" class="">1</font></div><div class=""><font face="Consolas" class="">2</font></div><div class=""><font face="Consolas" class="">3</font></div><div class=""><font face="Consolas" class="">4</font></div><div class=""><font face="Consolas" class="">[0, 1, 2]</font></div><div class=""><font face="Consolas" class="">>>> </font></div></div><div class=""><br class=""></div><div apple-content-edited="true">
<div style="color: rgb(0, 0, 0); font-family: Helvetica; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; " class=""><br class="Apple-interchange-newline"><br class="Apple-interchange-newline"><br class=""></div><div style="color: rgb(0, 0, 0); font-family: Helvetica; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; " class=""><br class=""></div><div style="color: rgb(0, 0, 0); font-family: Helvetica; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; " class=""><br class=""></div><div style="color: rgb(0, 0, 0); font-family: Helvetica; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; " class="">Shane Green </div><div style="color: rgb(0, 0, 0); font-family: Helvetica; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; " class=""><a href="http://www.umbrellacode.com">www.umbrellacode.com</a></div><div style="color: rgb(0, 0, 0); font-family: Helvetica; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; " class="">408-692-4666 | <a href="mailto:shane@umbrellacode.com">shane@umbrellacode.com</a></div>
</div>
<br class=""><div><div class="">On Jan 29, 2013, at 8:23 AM, Zachary Ware <<a href="mailto:zachary.ware+pyideas@gmail.com">zachary.ware+pyideas@gmail.com</a>> wrote:</div><br class="Apple-interchange-newline"><blockquote type="cite"><p class=""><br class="">
On Jan 29, 2013 10:02 AM, "Oscar Benjamin" <<a href="mailto:oscar.j.benjamin@gmail.com" class="">oscar.j.benjamin@gmail.com</a>> wrote:<br class="">
><br class="">
> On 29 January 2013 15:34, Zachary Ware <<a href="mailto:zachary.ware%2Bpyideas@gmail.com" class="">zachary.ware+pyideas@gmail.com</a>> wrote:<br class="">
> ><br class="">
> > On Jan 29, 2013 9:26 AM, "Oscar Benjamin" <<a href="mailto:oscar.j.benjamin@gmail.com" class="">oscar.j.benjamin@gmail.com</a>><br class="">
> > wrote:<br class="">
> >><br class="">
> >> On 29 January 2013 11:51, yoav glazner <<a href="mailto:yoavglazner@gmail.com" class="">yoavglazner@gmail.com</a>> wrote:<br class="">
> >> > Here is very similar version that works (tested on python27)<br class="">
> >> >>>> def stop():<br class="">
> >> > next(iter([]))<br class="">
> >> ><br class="">
> >> >>>> list((i if i<50 else stop()) for i in range(100))<br class="">
> >> > [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,<br class="">
> >> > 20,<br class="">
> >> > 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,<br class="">
> >> > 39,<br class="">
> >> > 40, 41, 42, 43, 44, 45, 46, 47, 48, 49]<br class="">
> >><br class="">
> >> That's a great idea. You could also do:<br class="">
> >> >>> list(i for i in range(100) if i<50 or stop())<br class="">
> >><br class="">
> >> It's a shame it doesn't work for list/set/dict comprehensions, though.<br class="">
> >><br class="">
> ><br class="">
> > I know I'm showing my ignorance here, but how are list/dict/set<br class="">
> > comprehensions and generator expressions implemented differently that one's<br class="">
> > for loop will catch a StopIteration and the others won't? Would it make<br class="">
> > sense to reimplement list/dict/set comprehensions as an equivalent generator<br class="">
> > expression passed to the appropriate constructor, and thereby allow the<br class="">
> > StopIteration trick to work for each of them as well?<br class="">
><br class="">
> A for loop is like a while loop with a try/except handler for<br class="">
> StopIteration. So the following are roughly equivalent:<br class="">
><br class="">
> # For loop<br class="">
> for x in iterable:<br class="">
> func1(x)<br class="">
> else:<br class="">
> func2()<br class="">
><br class="">
> # Equivalent loop<br class="">
> it = iter(iterable)<br class="">
> while True:<br class="">
> try:<br class="">
> x = next(it)<br class="">
> except StopIteration:<br class="">
> func2()<br class="">
> break<br class="">
> func1(x)<br class="">
><br class="">
> A list comprehension is just like an implicit for loop with limited<br class="">
> functionality so it looks like:<br class="">
><br class="">
> # List comp<br class="">
> results = [func1(x) for x in iterable if func2(x)]<br class="">
><br class="">
> # Equivalent loop<br class="">
> results = []<br class="">
> it = iter(iterable)<br class="">
> while True:<br class="">
> try:<br class="">
> x = next(it)<br class="">
> except StopIteration:<br class="">
> break<br class="">
> # This part is outside the try/except<br class="">
> if func2(x):<br class="">
> results.append(func1(x))<br class="">
><br class="">
> The problem in the above is that we only catch StopIteration around<br class="">
> the call to next(). So if either of func1 or func2 raises<br class="">
> StopIteration the exception will propagate rather than terminate the<br class="">
> loop. (This may mean that it terminates a for loop higher in the call<br class="">
> stack - which can lead to confusing bugs - so it's important to always<br class="">
> catch StopIteration anywhere it might get raised.)<br class="">
><br class="">
> The difference with the list(generator) version is that func1() and<br class="">
> func2() are both called inside the call to next() from the perspective<br class="">
> of the list() function. This means that if they raise StopIteration<br class="">
> then the try/except handler in the enclosing list function will catch<br class="">
> it and terminate its loop.<br class="">
><br class="">
> # list(generator)<br class="">
> results = list(func1(x) for x in iterable if func2(c))<br class="">
><br class="">
> # Equivalent loop:<br class="">
> def list(iterable):<br class="">
> it = iter(iterable)<br class="">
> results = []<br class="">
> while True:<br class="">
> try:<br class="">
> # Now func1 and func2 are both called in next() here<br class="">
> x = next(it)<br class="">
> except StopIteration:<br class="">
> break<br class="">
> results.append(x)<br class="">
> return results<br class="">
><br class="">
> results_gen = (func1(x) for x in iterable if func2(x))<br class="">
> results = list(results_gen)<br class="">
></p><p class="">That makes a lot of sense. Thank you, Oscar and Joao, for the explanations. I wasn't thinking in enough scopes :)</p><p class="">Regards,</p><p class="">Zach Ware<br class="">
</p>
_______________________________________________<br class="">Python-ideas mailing list<br class=""><a href="mailto:Python-ideas@python.org">Python-ideas@python.org</a><br class="">http://mail.python.org/mailman/listinfo/python-ideas<br class=""></blockquote></div><br class=""></body></html>