[Python-ideas] Reduce/fold and scan with generator expressions and comprehensions
Steven D'Aprano
steve at pearwood.info
Sun Oct 23 20:29:41 EDT 2016
On Sun, Oct 23, 2016 at 02:10:42PM -0200, Danilo J. S. Bellini wrote:
> >
> > Ah, that makes SIX existing solutions. Do we need a seventh?
>
> It might have dozens of solutions, perhaps an infinity of solutions.
> Brainfuck and assembly can be used, or even turing machine instructions...
No, those are *implementations*. You can implement your solution in any
language you like. For integration with Python, any of C, Fortran, Rust,
Julia, Cython and (of course) pure Python are proven to work well. Using
Turing Machine instructions requires some sort of TM compiler... good luck with
that.
But you cut out the most important part of my post. You've given lots of
existing solutions. Why aren't they satisfactory? Even if somebody wants
to write in a functional style, the reduce() solution you show seems
perfectly clean and conventional to anyone used to functional code:
from functools import reduce
reduce(lambda prev, x: abs(prev - x), [3, 4, 5], 2)
returns 2. What is wrong with this solution? That is the obvious
solution for somebody looking for a functional style: something called
reduce or fold. And there's no harm in needing to import reduce. Not
every function has to be a built-in.
Whereas your suggestion needs TWO new features: new syntax:
(abs(prev - x) for x in [3, 4, 5] from prev = 2)
plus a new built-in function last() which extracts the final value from
an iterator. That means you will risk encouraging people to wastefully
generate a large container of unneeded and unwanted intermediate values:
last([abs(prev - x) for x in range(100000) from prev = 2])
which will generate a list 100000 items long just to extract the final
one. reduce() is better, that is exactly what reduce() is designed for.
> But there should be one, and preferably only one, OBVIOUS way to do it.
> Readability counts.
Right. And the obvious way is the imperative approach (only this time I
will use a better variable name):
py> result = 2
py> for x in [3, 4, 5]:
... result = abs(result - x)
...
py> result
2
For those who think in functional programming terms, reduce() is the
obvious way.
Also, I feel that your proposal could have been explained better. I felt
overloaded by the sheer mass of different alternatives, and mislead by
your use of the name "prev" for something that I see now on more careful
reading is *not* the previous value of the loop variable (as I first
understood) but the running calculation result.
In fairness I am sick and if I were well I may have been able to keep
this straight in my head, but calling the variable "prev" is actively
misleading. I was mislead, and (I think) Chris who just suggested this
was similar to the SQL "lag" function may have been mislead as well. (Or
perhaps he was just mislead by me, in which case, sorry Chris!)
--
Steve
More information about the Python-ideas
mailing list