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

Peter O'Connor peter.ed.oconnor at gmail.com
Mon Apr 16 10:08:50 EDT 2018

In any case, although I find the magic variable-injection stuff quite
strange, I like the decorator.

Something like

    @scannable(average=0)  # Wrap function so that it has a "scan" method
which can be used to generate a stateful scan object
    def exponential_moving_average(average, x, decay):
        return (1-decay)*average + decay*x

    stateful_func = exponential_moving_average.scan(average=initial)
    smooth_signal = [stateful_func(x) for x in signal]

Seems appealing because it allows you to define the basic function without,
for instance, assuming that decay will be constant. If you wanted dynamic
decay, you could easily have it without changing the function:

    stateful_func = exponential_moving_average.scan(average=initial)
    smooth_signal = [stateful_func(x, decay=decay) for x, decay in
zip(signal, decay_schedule)]

And you pass around state explicitly.

On Mon, Apr 16, 2018 at 9:49 AM, Peter O'Connor <peter.ed.oconnor at gmail.com>

> Hi Danilo,
> The idea of decorating a function to show that the return variables could
> be fed back in in a scan form is interesting and could solve my problem in
> a nice way without new syntax.
> I looked at your code but got a bit confused as to how it works (there
> seems to be some magic where the decorator injects the scanned variable
> into the namespace).  Are you able to show how you'd implement the moving
> average example with your package?
> 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.
> - Peter
> On Sat, Apr 14, 2018 at 3:57 PM, Danilo J. S. Bellini <
> danilo.bellini at gmail.com> wrote:
>> On 5 April 2018 at 13:52, Peter O'Connor <peter.ed.oconnor at gmail.com>
>> wrote:
>>> I was thinking it would be nice to be able to encapsulate this common
>>> type of operation into a more compact comprehension.
>>> I propose a new "Reduce-Map" comprehension that allows us to write:
>>> signal = [math.sin(i*0.01) + random.normalvariate(0, 0.1) for i in range(1000)]
>>> smooth_signal = [average = (1-decay)*average + decay*x for x in signal from average=0.]
>>> Instead of:
>>> def exponential_moving_average(signal: Iterable[float], decay: float, initial_value: float=0.):
>>>     average = initial_value
>>>     for xt in signal:
>>>         average = (1-decay)*average + decay*xt
>>>         yield average
>>> signal = [math.sin(i*0.01) + random.normalvariate(0, 0.1) for i in range(1000)]
>>> smooth_signal = list(exponential_moving_average(signal, decay=0.05))
>>> I wrote in this mail list the very same proposal some time ago. I was
>> trying to let the scan higher order function (itertools.accumulate with a
>> lambda, or what was done in the example above) fit into a simpler list
>> comprehension.
>> As a result, I wrote this project, that adds the "scan" feature to Python
>> comprehensions using a decorator that performs bytecode manipulation (and
>> it had to fit in with a valid Python syntax):
>> https://github.com/danilobellini/pyscanprev
>> In that GitHub page I've wrote several examples and a rationale on why
>> this would be useful.
>> --
>> 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/20180416/2773bbd6/attachment-0001.html>

More information about the Python-ideas mailing list