There are a couple minor errors in Nathaniels (presumably untested) code.  But I think it looks quite elegant overall, actually:

#!/usr/bin/env python3
from string import ascii_lowercase
from random import random

class PushbackAdaptor(object):
    def __init__(self, iterable):
        self.base = iter(iterable)
        self.stack = []

    def __next__(self):
        if self.stack:
            return self.stack.pop()
        else:
            return next(self.base)

    def pushback(self, obj):
        self.stack.append(obj)

    def __iter__(self):
        return self

def repeat_some(it):
    it = PushbackAdaptor(it)
    for x in it:
        print(x, end='')
        if random() > 0.5:
            it.pushback(x)
            continue
    print()

repeat_some(ascii_lowercase)
repeat_some(range(10))

On Wed, Sep 24, 2014 at 6:50 PM, Nathaniel Smith <njs@pobox.com> wrote:

On 25 Sep 2014 02:09, "Andrew Barnert" <abarnert@yahoo.com.dmarc.invalid> wrote:
>
> On Sep 24, 2014, at 15:09, Cathal Garvey <cathalgarvey@cathalgarvey.me> wrote:
>
> >
> > The conversation began with me complaining that I'd like a third mode of
> > explicit flow control in Python for-loops; the ability to repeat a loop
> > iteration in whole. The reason for this was that I was parsing data
> > where a datapoint indicated the end of a preceding sub-group, so the
> > datapoint was both a data structure indicator *and* data in its own
> > right. So I'd like to have iterated over the line once, branching into
> > the flow control management part of an if/else, and then again to branch
> > into the data management part of the same if/else.
> >
> > Yes, this is unnecessary and just a convenience for parsing that I'd
> > like to see.
>
> It would really help to have specific use cases, so we can look at how much the syntactic sugar helps readability vs. what we can write today. Otherwise all anyone can say is, "Well, it sounds like it might be nice, but I can't tell if it would be nice enough to be worth a language change", or try to invent their own use cases that might not be as nice as yours and then unfairly dismiss it as unnecessary.

The way I would describe this is, the proposal is to add single-item pushback support to all for loops. Tokenizers are a common case that needs pushback ("if we are in the IDENTIFIER state and the next character is not alphanumeric, then set state to NEW_TOKEN and process it again").

I don't know how common such cases are in the grand scheme of things, but they are somewhat cumbersome to handle when they come up.

The most elegant solution I know is:

class PushbackAdaptor:
    def __init__(self, iterable):
        self.base = iter(iterable)
        self.stack = []

    def next(self):
        if self.stack:
            return self.stack.pop()
        else:
            return self.base.next()

    def pushback(self, obj):
        self.stack.append(obj)

it = iter(character_source)
for char in it:
    ...
    if state is IDENTIFIER and char not in IDENT_CHARS:
        state = NEW_TOKEN
        it.push_back(char)
        continue
    ...

In modern python, I think the natural meaning for 'continue with' wouldn't be to special-case something like this. Instead, where 'continue' triggers a call to 'it.next()', I'd expect 'continue with x' to trigger a call to 'it.send(x)'. I suspect this might enable some nice idioms in coroutiney code, though I'm not very familiar with such.

-n


_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/



--
Keeping medicines from the bloodstreams of the sick; food
from the bellies of the hungry; books from the hands of the
uneducated; technology from the underdeveloped; and putting
advocates of freedom in prisons.  Intellectual property is
to the 21st century what the slave trade was to the 16th.