[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [sc-users] Difference between Plazy and Pfunc (and PlazyEnvir) in practice



On Fri, Jan 18, 2019 at 5:40 PM <mail@xxxxxxxxxxxxxxxxx> wrote:
> Something that I’ve wondered lately is what the difference between Plazy and Pfunc actually is, especially in practice: When to use what.
>
> The help files for both classes are a bit sparse in this regard:
>
> Pfunc: Function pattern
> Description: Returns a Stream that returns values from the nextFunc.
>
> Plazy: instantiate new patterns from a function
> Description: Plazy evaluates a function that returns a pattern and embeds it in a stream.

Indeed, those help descriptions are appalling. (And Pfunc's is inaccurate.)

So...

Remember that patterns don't do any work by themselves -- like a
blueprint for a house, which tells you the shape and size of each
room, and the rooms' layout, but doesn't build the house.

Streams do the work. Patterns have two ways to get into a stream:
`asStream`, and `embedInStream`. `asStream` just makes a new stream
from the pattern template. `embedInStream` assumes that you're already
in the context of a stream, and you just want the pattern to do its
work as part of the existing stream.

If you have Pseq([Pwhite(0, 4, 1), Pwhite(10, 14, 1)], inf) in a
Pbind, the Pbind has to use this as a stream, so it calls .asStream on
the outer layer (Pseq). Then, when Pbind requests a value from this
stream, Pseq "embeds" the first item, which is a pattern -- so it uses
embedInStream. After Pwhite returns its one value, it returns control
back to Pseq, which advances to the next item and does embedInStream,
and so on.

Pfunc's stream is very simple: Call the function, and whatever the
function returned, that is the stream's 'next' value.

p = Pfunc { rrand(0, 4) };
q = p.asStream;  // "a FuncStream"
q.next;

Plazy's function does not return the final values. It returns a
pattern -- and this pattern is embedded in the stream, effectively "in
place of" Plazy.

p = Plazy { Pseries(0, 1, inf) };
q = p.asStream;
q.next;

p = Pseries(0, 1, inf);  // identical result!
q = p.asStream;
q.next;

^^ Plazy returns an instance of Pseries, and then the stream acts as
if Pseries were the point all along. In a simple case like this (with
just one Plazy), you don't need Plazy at all.

Plazy gets interesting when it will be embedded multiple times --
because, every time Plazy gets embedded, it re-evaluates its function.
So Plazy can substitute a different pattern for itself every time.

p = Pn(
    Plazy { Pseries(rrand(0, 5), 1, rrand(2, 4)) },
    inf
);
p.asStream.nextN(15)
-> [ 1, 2, 3, 4, 0, 1, 2, 3, 2, 3, 4, 5, 1, 2, 5 ]

// BUT...
p = Pfunc { Pseries(0, 1, inf) };
q = p.asStream;
q.next;
-> a Pseries

Remember that Pfunc's function returns the final values. If that
function returns a pattern, then the pattern becomes the output value
directly. Probably not what you want.

// ALSO...
Pfunc { rrand(0, 4) }.asStream.nextN(5)
-> [ 3, 2, 2, 1, 4 ]

^^ OK, makes sense, Pfunc calls the function repeatedly and returns a
new random number each time.

Plazy { rrand(0, 4) }.asStream.nextN(5)
-> [ 0, nil, nil, nil, nil ]

^^ Also makes sense. Plazy "embeds" the result of its function. If you
embed a simple number in the stream, the number is embedded one and
only one time. So, after one value, the stream has run out of things
to do, and if you keep asking for values, you get nil.

Does that help?
hjh

_______________________________________________
sc-users mailing list

info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mailinglist.aspx
archive: https://listarc.bham.ac.uk/marchives/sc-users/
search: https://listarc.bham.ac.uk/lists/sc-users/search/