* correction to example: moving_average_gen = (average:= moving_average_step(average, x, decay=decay) for x in signal from average=initial)
On Thu, Apr 12, 2018 at 3:37 PM, Peter O'Connor <peter.ed.ocon...@gmail.com> wrote: > On Wed, Apr 11, 2018 at 10:50 AM, Paul Moore <p.f.mo...@gmail.com> wrote: > >> In particular, I'm happiest with the named moving_average() function, >> which may reflect to some extent my lack of familiarity with the >> subject area. I don't *care* how it's implemented internally - an >> explicit loop is fine with me, but if a domain expert wants to be >> clever and use something more complex, I don't need to know. An often >> missed disadvantage of one-liners is that they get put inline, meaning >> that people looking for a higher level overview of what the code does >> get confronted with all the gory details. > > > I'm all in favour of hiding things away into functions - I just think > those functions should be as basic as possible, without implicit > assumptions about how they will be used. Let me give an example: > > ---- > > Lets look at your preferred method (A): > > def moving_average(signal_iterable, decay, initial=0): > last_average = initial > for x in signal_iterable: > last_average = (1-decay)*last_average + decay*x > yield last_average > > moving_average_gen = moving_average(signal, decay=decay, > initial=initial) > > And compare it with (B), which would require the proposed syntax: > > def moving_average_step(last_average, x, decay): > return (1-decay)*last_average + decay*x > > moving_average_gen = (average:= moving_average_step(average, x, > decay=decay) for x in signal from x=initial) > > ----- > > Now, suppose we want to change things so that the "decay" changes with > every step. > > The moving_average function (A) now has to be changed, because what we > once thought would be a fixed parameter is now a variable that changes > between calls. Our options are: > - Make "decay" another iterable (in which case other functions calling > "moving_average" need to be changed). > - Leave an option for "decay" to be a float which gets transformed to an > iterable with "decay_iter = (decay for _ in itertools.count(0)) if > isinstance(decay, (int, float)) else decay". (awkward because 95% of > usages don't need this. If you do this for more parameters you suddenly > have this weird implementation with iterators everywhere even though in > most cases they're not needed). > - Factor out the "pure" "moving_average_step" from "moving_average", and > create a new "moving_average_with_dynamic_decay" wrapper (but now we have > to maintain two wrappers - with the duplicated arguments - which starts to > require a lot of maintenance when you're passing down several parameters > (or you can use the dreaded **kwargs). > > With approach (B) on the other hand, "moving_average_step" and all the > functions calling it, can stay the same: we just change the way we call it > in this instance to: > > moving_average_gen = (average:= moving_average_step(average, x, > decay=decay) for x, decay in zip(signal, decay_schedule) from x=initial) > > ---- > > Now lets imagine this were a more complex function with 10 parameters. I > see these kind of examples a lot in machine-learning and robotics programs, > where you'll have parameters like "learning rate", "regularization", > "minibatch_size", "maximum_speed", "height_of_camera" which might initially > be considered initialization parameters, but then later it turns out they > need to be changed dynamically. > > This is why I think the "(y:=f(y, x) for x in xs from y=initial)" syntax > can lead to cleaner, more maintainable code. > > > > On Wed, Apr 11, 2018 at 10:50 AM, Paul Moore <p.f.mo...@gmail.com> wrote: > >> On 11 April 2018 at 15:37, Peter O'Connor <peter.ed.ocon...@gmail.com> >> wrote: >> >> > If people are happy with these solutions and still see no need for the >> > initialization syntax, we can stop this, but as I see it there is a >> "hole" >> > in the language that needs to be filled. >> >> Personally, I'm happy with those solutions and see no need for the >> initialisation syntax. >> >> In particular, I'm happiest with the named moving_average() function, >> which may reflect to some extent my lack of familiarity with the >> subject area. I don't *care* how it's implemented internally - an >> explicit loop is fine with me, but if a domain expert wants to be >> clever and use something more complex, I don't need to know. An often >> missed disadvantage of one-liners is that they get put inline, meaning >> that people looking for a higher level overview of what the code does >> get confronted with all the gory details. >> >> Paul >> > > > >
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/