# [Python-ideas] Proposal: A Reduce-Map Comprehension and a "last" builtin

Peter O'Connor peter.ed.oconnor at gmail.com
Thu May 24 08:06:03 EDT 2018

```To give this old horse a kick: The "given" syntax in the recent thread
could give a nice solution for the problem that started this thread.

smooth_signal = [average := (1-decay)*average + decay*x for x in signal
from average=0.]

We could use given for both the in-loop variable update and the variable
initialization:
smooth_signal =  [average given average=(1-decay)*average + decay*x for
x in signal] given average=0.

This especially makes sense for the extended syntax, where my proposal of:
(z, y := f(z, x) -> y for x in iter_x from z=initial_z)

Becomes:
(y given z, y = f(z, x) for x in iter_x) given z=initial_z

So in stead of adding 2 symbols and a keyword, we just need to add the one
"given" keyword.

It's worth noting, as Serhiy pointed out, that this is already supported in
python, albeit with a very clunky syntax:

smooth_signal = [average for average in [0] for x in signal for average
in [(1-decay)*average + decay*x]]

(y for z in [initial_z] for x in iter_x for z, y in [f(z, x)])

On Tue, Apr 17, 2018 at 12:02 AM, Danilo J. S. Bellini <
danilo.bellini at gmail.com> wrote:

> On 16 April 2018 at 10:49, Peter O'Connor <peter.ed.oconnor at gmail.com>
> wrote:
>
>> Are you able to show how you'd implement the moving average example with
>>
>
> Sure! The single pole IIR filter you've shown is implemented here:
> https://github.com/danilobellini/pyscanprev/blob/
> master/examples/iir-filter.rst
>
> I tried:
>>
>>     @enable_scan("average")
>>     def exponential_moving_average_pyscan(signal, decay, initial=0):
>>         yield from ((1-decay)*(average or initial) + decay*x for x in
>> signal)
>>
>>
>>     smooth_signal_9 = list(exponential_moving_average_pyscan(signal,
>> decay=decay))[1:]
>>
>> Which almost gave the right result, but seemed to get the initial
>> conditions wrong.
>>
>
> I'm not sure what you were expecting. A sentinel as the first "average"
> value?
>
> Before the loop begins, this scan-generator just echoes the first input,
> like itertools.accumulate.
> That is, the first value this generator yields is the first "signal"
> value, which is then the first "average" value.
>
> To put an initial memory state, you should do something like this (I've
> removed the floating point trailing noise):
>
> >>> from pyscanprev import enable_scan, prepend
> >>>
> >>> @enable_scan("y")
> >>> def iir_filter(signal, decay, memory=0):
> ...     return ((1 - decay) * y + decay * x for x in prepend(memory,
> signal))
> ...
> >>> list(iir_filter([1, 2, 3, 2, 1, -1, -2], decay=.1, memory=5))
> [5, 4.6, 4.34, 4.206, 3.9854, 3.68686, 3.218174, 2.6963566]
>
> In that example, "y" is the "previous result" (a.k.a. accumulator, or
> what had been called "average" here).
>
> --
> Danilo J. S. Bellini
> ---------------
> "*It is not our business to set up prohibitions, but to arrive at
> conventions.*" (R. Carnap)
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20180524/0c9706b5/attachment-0001.html>
```