On Aug 9, 2019, at 08:47, Peter O'Connor <peter.ed.ocon...@gmail.com> wrote: > > I've often found that it would be useful for the following type of expression > to be condensed to a one-liner: > > def running_average(x_seq): > averages = [] > avg = 0 > for t, x in enumerate(x_seq): > avg = avg*t/(t+1) + x/(t+1) > averages.append(avg) > return averages > > Because really, there's only one line doing the heavy lifting here, the rest > is kind of boilerplate.
It seems like the only reason you can’t write this with accumulate is that accumulate doesn’t take a start value like reduce does? And I think this would be a lot clearer and more readable, especially if you’re doing this kind of thing more than once: def running(xs, func, start): yield from accumulate(enumerate(xs), lambda avg, told: func(avg, *tx), start) def running_average(xs, func): yield from running(xs, lambda avg, t, x: avg*t/(t+1) + x/(t+1), 0.0) Now the part that does the heavy lifting is all in one place and just does what it’s says, without being confusingly interleaved with the boilerplate. Plus, the parts of the boilerplate that are reusable are abstracted into functions (accumulate and running) that can be reused, while the rest of it has vanished. (This is one of those rare cases where 2.x-style decomposing def/lambda was actually useful, but if that extra lambda in running really bothers you, that’s another one-liner HOF you can abstract out trivially and reuse.) More generally, it’s a lot easier to use comprehensions and higher order functions if your algorithm can be written in terms of “generate the next immutable value” instead of “update the mutable variable”, and I don’t think that’s a limitation of the language. Comprehensions are much more readable when they’re declarative than when they’re for statements in disguise. Also, I don’t think the reason people were objecting to your four-clause comprehension was that it wasn’t easy enough to tell that the innermost clause only “loops” exactly one time, but that it’s a comprehension with four clauses in the first place. Changing the spelling of that clause to make the no-actual-looping doesn’t solve that. Finally, you can already play tricks with the walrus operator to avoid moving things like initialization outside the comprehension, just as you could with your proposed syntax. For example, “for t, x in (avg:=0) or enumerate(xs)” is a perfectly valid clause that assigns 0 to avg and then loops over the enumerate, and it doesn’t require you to turn one for clause into two. But it still adds just as much complexity for the reader to deal with, so I think you’re still better off not doing it. (As a side note, you probably want a numerical stable average like the ones in statistics or numpy, rather than one that accumulates float rounding errors indiscriminately, but that’s another issue.) _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/3RPVRDFALMRMYHWYLCP4EPTOI4LKQIZY/ Code of Conduct: http://python.org/psf/codeofconduct/