<div class="gmail_quote"><div class="gmail_quote"><div class="im">On Sun, Jun 10, 2012 at 7:01 PM, Steven D'Aprano <span dir="ltr"><<a href="mailto:steve@pearwood.info" target="_blank">steve@pearwood.info</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div>Andrew Carter wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Forgive me for any problems in this e-mail as I'm new to this mailing list.<br>
<br>
I thought it might be nice to be able to somehow save a state in<br>
list/generator comprehensions,<br>
a side effect of this (although not the intended goal) is it would make<br>
reduce feasible in a clean manner as the final result would just be the<br>
state.<br>
</blockquote>
<br></div>
reduce already exists; in Python 2, it is a built-in available at all times, in Python 3 it has been banished to the functools module.<br>

<br></blockquote></div><div>I think that it is was banished for good reason. I have found myself sometimes writing code that needs a reduce, but I feel that using the reduce function provided isn't very clear, especially if you see it and aren't familiar with the function. Admittedly writing a function that is a few short lines is possible, and what I end up doing, it just seems like there should be a more elegant way to do it than having a bunch of specialized functions.</div>
<div class="im">
<div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
What is your use-case for this? "Saving state" is a means to an end. The beauty of list comprehensions and generator expressions is that they are intentionally quite simple and limited. If you need something more complex, write a function or generator. Not everything has to be a (very-long and unreadable) one-linear.<br>


<br>
reduce already exists, but if it didn't, you could write it quite easily. Here's a version with optional starting value which yields the intermediate results:<br></blockquote></div><div>As I have mentioned above occasionally I want to turn a list into a single value by some repeated operation, but actually I think its more common that I want to map some operation over a list with dependencies of previous operation passed through. I think my most common use case, is I have a function that operates on a single value, and also has some state. Unfortunately leaving my example purposely vague, I was iterating over a list, and had what was effectively an environment variable (more of state) initially as a dynamic environment, so it was updated each time the function was called across the list comprehension. I then for other reasons wanted to use the environment type as a key for dictionary (which is a problem if its mutable), but that meant that the original list comprehension (which I felt was rather simple). Admittedly it didn't take me any time at all to write a simple function that did the list comprehension, but it still felt like a simple enough problem that it could be elegantly solved without resorting to the helper function.</div>
<div><div class="h5">
<div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
import itertools<br>
_MISSING = object()  # sentinel value<br>
<br>
def foldl(func, iterable, start=_MISSING):<br>
    # foldr is left as an exercise :-)<br>
    if start is _MISSING:<br>
        it = iter(iterable)<br>
    else:<br>
        it = itertools.chain([start], iterable)<br>
    a = next(it)  # raises if iterable is empty and start not given<br>
    try:<br>
        b = next(it)<br>
    except StopIteration:<br>
        yield a<br>
        return<br>
    a = func(a, b)<br>
    yield a<br>
    for b in it:<br>
        a = func(a, b)<br>
        yield a<br>
<br>
<br>
Modifying this to return just the last value is easy, and in fact is simpler than the above:<br>
<br>
def foldl(func, iterable, start=_MISSING):<br>
    if start is _MISSING:<br>
        it = iter(iterable)<br>
    else:<br>
        it = itertools.chain([start], iterable)<br>
    a = next(it)<br>
    for b in it:<br>
        a = func(a, b)<br>
    return a<br>
<br>
<br>
<br>
[...]<div><br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Some of the pros of doing it this way is that because with/as are already<br>
keywords in python backwards compatibility shouldn't be an issue,<br>
</blockquote>
<br></div>
That's not an argument in favour of your request. That's merely the lack of one specific argument against it. There are an infinite number of things which could be done that won't break backwards compatibility, but that doesn't mean we should do them all. What positive arguments in favour of your proposal do you have? What does your proposal allow us to do that we can't already do, or at least do better?<div>

<span style="background-color:transparent"> </span></div></blockquote></div></div><div> I feel like there is a need from personal experience of mapping with state in a short concise way. However it is quite possible that it is just me, and I need to think about the problem differently, or perhaps live with 4 line functions that are only used once. As for the backwards compatibility I think was getting ahead of myself, I feel the with/as solution is quite clunky, but I couldn't come up with a more elegant solution that operated in a similar vein to how python feels as a language.</div>
<div class="im">
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
p.s. Is there a built-in way to get the last element from a generator<br>
(perhaps even with a default) a quick google search did not reveal one?<br>
</blockquote>
<br></div>
The same as you would get the last element from any iterator, not just generators: iterate over it as quickly as possible, keeping only the last value seen. Because generator values are generated lazily as needed, there's no direct way to skip to the last value, or get random access to them.<br>


<br>
In pure Python:<br>
<br>
for x in iterator:<br>
    pass<br>
<br>
This may be faster:<br>
<br>
collections.deque(iterator, maxlen=1)[0]<br>
<br></blockquote></div><div><span style="background-color:transparent">That's a neat solution, a little bit confusing at first glance, but still very neat, thanks!</span></div><div class="im"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">


Of course, both examples assume that the iterator or generator yields at least one value, and is not infinite.<span><font color="#888888"><br>
<br>
<br>
<br>
-- <br>
Steven<br>
<br>
______________________________<u></u>_________________<br>
Python-ideas mailing list<br>
<a href="mailto:Python-ideas@python.org" target="_blank">Python-ideas@python.org</a><br>
<a href="http://mail.python.org/mailman/listinfo/python-ideas" target="_blank">http://mail.python.org/<u></u>mailman/listinfo/python-ideas</a><br>
</font></span></blockquote></div></div><br>
</div><br>