[Python-ideas] Reduce/fold and scan with generator expressions and comprehensions

Steven D'Aprano steve at pearwood.info
Tue Oct 25 11:00:31 EDT 2016


On Tue, Oct 25, 2016 at 05:18:46AM -0200, Danilo J. S. Bellini wrote:

> > [...]. From what
> > I remember, the conclusion reached was that there are too
> > many degrees of freedom to be able to express reduction
> > operations in a comprehension-like way that's any clearer

Danilo, if you are going to quote somebody, especially when you copy and 
paste their words out of a completely different email from the one you 
are replying to, please tell us who you are quoting. It is not polite to 
quote somebody without attribution and out of context.


> I don't know if that's a conclusion from any other thread, but that's
> wrong. The only extra "freedom" required a way to access the previous
> output (or "accumulator", "memory", "state"... you can use the name you
> prefer, but they're all the same). How many parameters does itertools.scan
> have? And map/filter?

I do not know which email thread is being talked about here, but the 
conclusion is correct. In the most general case, you might want:

- the current value of some running total or result;
- the previous value of the running result;
- the value of the running result before that ("previous previous");
- and so on;
- more than one running calculation at the same time;
- the current sequence value (for x in [1, 2, 3]);
- the previous sequence value (previous value of x);
- the sequence value before that ("previous previous x");
- etc.

Recurrence relations are not all linear, and they don't always involve 
only the previous value. Fibonacci numbers need to track two previous 
running values:

F[n] = F[n-1] + F[n-2]

The Lagged Fibonacci generator is a pseudo-random number generator that 
uses a similar recurence, except instead of only needing to remember the 
previous two results, it remembers some arbitrary N previous results. 
E.g. we might say:

S[n] = S[n - 4] + S[n - 9]

and so we use the 4th-previous and 9th-previous result to generate the 
new one. Just having the current running result is not sufficient.


[...]
> Forget the word "reduce", some people here seem to have way too much taboo
> with that word, and I know there are people who would prefer a higher
> McCabe complexity just to avoid it. Perhaps there are people who prefer
> masochist rituals instead of using "reduce", who knows?

And perhaps there are people who think that using "reduce" and other 
functional idioms instead of good, clean, easy-to-understand, easy-to- 
debug imperative code is a "masochist ritual".


> but I
> wrote a lot about the scan use cases and no one here seem to have read what
> I wrote, and the only reason that matters seem to be a kind of social
> status, not really "reason". I probably wrote way more reasons for that
> proposal than annotations could ever have.

I read your use-cases. I went to the Github page and looked at the 
examples and the documentation. I didn't comment because it doesn't 
matter whether there is one use-case or a million use-cases, the 
fundamental question still applies: why do we need ANOTHER way of 
solving these problems when there are already so many? More use-cases 
doesn't answer that question. A million weak use-cases is still weak.

The obvious way to solve these use-cases is still an imperative 
for-loop. Python is not Haskell, it is not a functional programming 
language. It has some functional programming features, but it is not and 
never will be intended for arbitrarily complex code to be written in a 
pure functional style.

Comprehensions are intentionally kept simple. They are not a substitute 
for all loops, only the easy 80% or 90%. As confirmed by Nick Coghlan, 
comprehensions are absolutely and fundamentally intended as syntactic 
sugar for ONE and ONLY one pattern. For example:

    [expr for x in seq for y in seq2 if cond]

is sugar for:

    result = []
    for x in seq:
        for y in seq2:
            if cond:
                result.append(expr)


If you need to keep the previous sequence item, or a running total, or 
break out of the loop early, or use a while loop, you cannot use a 
comprehension.

So I'm afraid that completely rules out your idea of having a running 
total or result in a comprehension. That simply isn't compatible with 
the design of comprehensions as ruled by Guido and the core devs.

Could you change their mind? It's not impossible. If you have an 
compelling answer to the questions "why does this need to be part of 
comprehension syntax? why not use a for-loop, or itertools.accumulate?" 
then of course they will reconsider. But the barrier is very high. It is 
not a matter of more use-cases. We already have solutions to those 
use-cases. You have to explain why the existing solutions don't work.



-- 
Steve


More information about the Python-ideas mailing list