Factoring Out Common Args To Zipped Generators
I'm playing around with some additive synthesis in Python.
I've implemented an oscillator as a generator that takes a number of parameters. It is then possible to mix multiple oscillators using zip (or better, itertools.izip) over them and doing a (weighted) sum.
However, I wanted to be able to factor out common arguments to the oscillators so I didn't have to specify the frequency of each one individually.
I knew functools.partial would be part of the solution but it took me a while to work out how to combine its use with generators and itertools.izip.
Here is a simplified progression of what I came up with. <h3>Phase 1</h3>
Rather than use oscillators, let's just imagine with have a generator that works a lot like xrange:
def gen1(start, stop, step):
n = start
while n <= stop:
yield n
n += step
then we can combine multiple generators and, say, sum the corresponding elements like this:
for x in zip(gen1(10, 20, 2), gen1(10, 25, 3)): print sum(x),<h3>Phase 2</h3>
Let's abstract this into a function that takes generators as arguments (and uses itertools.izip)
def mixer1(*generators):
return (sum(x) for x in izip(*generators))
for x in mixer1(gen1(10, 20, 2), gen1(10, 25, 3)): print x,
mixer1 is similar to my mixer (although without weighting) <h3>Phase 3</h3>
But now say we wanted to factor out the common start parameter. First we need a partial version of function gen1:
gen2 = lambda **kwargs: partial(gen1, **kwargs)
This allows one to say
partial_gen = gen2(stop=20, step=2)
and then later call
partial_gen(start=10)
to get the generator.
But what we now need is a new version of the mixer that takes the extra keyword args and passes them in to each partial function to turn them back into generators:
def mixer2(*generators, **kwargs):
return mixer1(*[gen(**kwargs) for gen in generators])
and now we can say:
for x in mixer2(gen2(stop=20, step=2), gen2(stop=25, step=3), start=10): print x,<h3>Phase 4</h3>
Here's the final version:
gen2 = lambda **kwargs: partial(gen1, **kwargs)def mixer3(*generators, **kwargs): return (sum(x) for x in izip(*[gen(**kwargs) for gen in generators]))
The real thing is a little more involved because of the weighted summing, etc but the hard parts are shown.
Syndicated 2008-05-02 23:28:24 (Updated 2008-05-03 00:03:41) from James Tauber
FOAF updates: Trust rankings are now exported, making the data available to other users and websites. An external FOAF URI has been added, allowing users to link to an additional FOAF file.
Keep up with the latest Advogato features by reading the Advogato status blog.
If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!