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  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 wrote:
On 16 April 2018 at 10:49, Peter O'Connor wrote:
Are you able to show how you'd implement the moving average example with your package?

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)