Pipe Operator | and 3 dots and 2 greater than

What EXCACTLY is the pipe in SC? : |

I see I can use it to add integers, it wraps arguments, but it also does other stuff?

Also I’ve seen 3 dots: …

What is that?

Also >> (2 greater than)

Sorry if this is obvious, I can’t find info in the help docs, I can’t figure it out by context, and experimenting hasn’t yielded much.

Examples below:

( Ndef(\bitshift, { var t = PulseCount.ar(Impulse.ar(8e3)); var sig = HPF.ar( ( ((t * 15) & (t >> 5)) | ((t * 5) & (t >> [3, 4])) | ((t * 2) & (t >> 9)) | ((t * 8) & (t >> 11)) - 3 % 256 ) / 127-1 * 3 , 20 ).distort * 0.2; sig = Greyhole.ar(sig, sig, 0.5, sig).flop.mean/4; }).play; )

and

( { var pC; // program counter var stack; var program = BBlockerProgram([6]).asLocalBuf; # pC ... stack = BBlockerBuf.ar( freq: MouseX.kr(1000, 20000, -2), bufnum: program, startpoint: 0 ); Splay.ar(SinOsc.ar(stack.linexp(-1, 1, 100, 1000))) + (MouseY.kr(0, 1) * pC!2); }.scope )

The << | & >> symbols are typically implemented as bitwise operators someone else will have to explain in more detail… I believe they deal with generating numbers, using binary format… though they might seem esoteric, I believe the main usage is for SC tweets, and staying below 140.

As it turns out, every one of your questions is answered in the same doc page, it’s called “Symbolic notations”

<< >> are either bitwise or Post << “IOstream”

| a single pipe will bitwise, a few fold/clip shortcuts, or call the union of two Set collection, or merge to Rect objects

& will bitwise or call the ‘intersection’ of two sets

|| is logical or, && is logical and… there’s only one way to use these:

`if( x > y && { true || { true } }) { \true } { \false } // you must put the second expression in function brackets, 100% of the time1

I would also put (x > y) in their own parentheses, but this isn’t 100% necessary.

If you aren’t used to control structures, my advice is stick to if and switch, because their syntax mirrors other:

if (true || { false }) { \true } {\false}

switch (x) { 1 } { \one } { 2 }{ \two }

or:

if( x > y, \true, \false) // in comma style, function brackets are optional

using the case statement is the same as using switch(true) { test }{ execute }, though it reads a little clearer

I prefer to the closed bracket syntax, it looks cleaner

there are a few different ways pipe symbols are used in function argument syntax, you’ll have to do your own testing on these

{ |x, n| }

{ |x n| }

{ | x = 8, n = 9| }

{ |x 8 n 9| }

{ |x …n| } // first arg is x, all args after x will be caught in an array, accessible as n within the function… this syntax never requires a comma (it will post an error warning, the comma in this case must be ommited)

The final version is:

{ |…n| } // there are only two ways to use these, either alone, or at the end of ones arg list.

If you aren’t completely familiar with function syntax, you can use this to run a few tests:
(
~use = {

|x 8 y ...z|

Post <<* [x, y] << $\n <<* z <<<* ($\n ! 3); // post everything

x.switch // same as switch (x)
{
    5
} {
      "five"
 } {
    8
} {
     #[ 8 ]
}; (\x -> x).postln; (\class: x.class).postln;

if(
    z.isEmpty
) { 
   z.reshape(4, 4).printAll
} {
     z.printAll
};

}

)

~use.value(8, nil, 100, 101, [102], [ [ 103 ] ])

~use.(5, nil, (…100))

~use.(y: (19, 29…89))

2 Likes

| is bitwise OR
3 | 2 // 1
& is bitwise AND
3 & 2 // 3
>> is bitwise right shift
3 >> 1 // 1
<< is bitwise left shift
3 << 1 // 6

1 Like

AH thank you both!. I knew it HAD to be in the help docs, I just didn’t know what to search for! The SC help really is pretty amazing. Thank you both so much for taking the time to answer! It’s a lot to digest .

Seriously thanks again. I’ve never been a developer and only dabbled in PS and bash scripting so this is pretty new to me. Your post makes sense though and is pretty clear…and I have to agree about the closed brackets.

For the sake of correctness – you’ve named it the operators correctly, but the results that you cite are not correct:

3 | 2 = 2r11 | 2r10 = 2r11 = 3.

3 & 2 = 2r11 & 2r10 = 2r10 = 2.

The bitwise operator where 3 vs 2 → 1 would be exclusive-or: 3 bitXor: 2 = 2r11 bitXor: 2r10 = 2r01 = 1.

It isn’t strictly true that the second expression must always be in function brackets.

In a > b or: (c > d), you must always have either () or {} because of SC’s left to right operator precedence. But a > b or: (c > d) is not illegal.

The difference is that () will always evaluate every condition, while {} will short circuit conditions that don’t need to be evaluated.

Why would a condition not need to be evaluated?

  • In and, if the first condition is false, then you already know the “and” result will be false – so there’s no need even to check the second.
  • In or, if the first condition is true, then you already know the “or” result will be true.

This is very helpful for conditions such as a > 0, but only if a.notNil:

  • if(a.notNil and: { a > 0 }) – if a is nil, then a > 0 is completely skipped, so it won’t throw an error, and life goes on.
  • if(a.notNil and: (a > 0))a > 0 will be evaluated in both cases – if a is nil, it will throw an error, and you get annoyed.

That is to say, it’s a good habit to use {} here. It isn’t a strict syntax rule but 99.99999999% of the time, {} is what you want.

Also, it’s twice as efficient to write compound Boolean expressions with and: and or: rather than && / || – literally twice!

{ a.notNil and: { a > 0 } }.def.dumpByteCodes
BYTECODES: (10)
  0   12       PushInstVar 'a'
  1   D3       SendSpecialUnaryArithMsg 'notNil'
  2   FA 00 04 JumpIfFalsePushFalse 4  (9)
  5   12       PushInstVar 'a'
  6   63       PushSpecialValue 0
  7   B0       TailCallReturnFromFunction
  8   E9       SendSpecialBinaryArithMsg '>'
  9   F2       BlockReturn

^^ The JumpIfFalsePushFalse is very fast.

{ a.notNil && { a > 0 } }.def.dumpByteCodes
BYTECODES: (8)
  0   12       PushInstVar 'a'
  1   D3       SendSpecialUnaryArithMsg 'notNil'
  2   04 01    PushLiteralX instance of FunctionDef - closed
  4   B0       TailCallReturnFromFunction
  5   A2 00    SendMsg '&&'
  7   F2       BlockReturn

SendMsg '&&' incurs method-call overhead (and this simply dispatches to and – so you get double method-call overhead). And, if a.notNil is true, then it has to evaluate the function that’s been pushed onto the stack – method call overhead again.

(
z = [nil, 1];

bench {
	1000000.do {
		var a = z.choose;
		a.notNil and: { a > 0 }
	}
};
)

time to run: 0.30233460800002 seconds.


(
z = [nil, 1];

bench {
	1000000.do {
		var a = z.choose;
		a.notNil && { a > 0 }
	}
};
)

time to run: 0.58998847399994 seconds.

&& is roughly a factor of two slower.

So IMO the optimal habit to get into is: if(condition1 and: { condition2 }) ....

hjh

2 Likes

You’re absolutely right… thanks for the clarification.

and: or: are twice is fast as && ||

and: or: && || only work with ( ) (execute regardless) or { execute only if needed }

Is it possible to make them both rapidly execute?

Whoa. Thanks for the clarification, although I didn’t understand half of it, and that’s ok.

There’s a slight difference between the two: because && is not optimized by the compiler, it can be used with function composition. and bypasses normal method dispatch, so it can be used only where the left-hand side has already been evaluated:

f = { 10.rand.odd } && { 10.rand.even };  // OK
f.value;

f = { 10.rand.odd } and: { 10.rand.even };
ERROR: Non Boolean in test.

… which might seem an exotic edge case, but let’s say you want, in a pattern, to eliminate notes where a counting index is divisible either by 3 or by 5:

(
p = Pbind(
	\index, Pseries(0, 1, inf),
	\type, Pif(
		(Pkey(\index) mod: 3 |==| 0) || (Pkey(\index) mod: 5 |==| 0),
		\rest,
		\note
	),
	\freq, (Pkey(\index) % 10 + 1) * 100,
	\dur, 0.125
).play;
)

p.stop;

(|==| is for equality with patterns or UGens. == always evaluates immediately on the given object. |==| composes with lazy streams to evaluate later.)

If you substitute or: for || in this example, you’ll get “ERROR: Message ‘or’ not understood.” That is, optimizing || and && to be the same as or: and and: would take away concrete functionality.

So I think, in this case, we have to leave it the way it is, and educate users better.

hjh

Hello James,
Evaluating your code example give me this:
ERROR: Message ‘|==|’ not understood.

on SC3.11.2
Have you got an idea on how to fix that?

According to Classlib: Add |==| lazy equality operator · supercollider/supercollider@0f6143f · GitHub, |==| was added into the development branch in October last year (about 7 months).

I had thought it was long enough ago that it would be in an official release by now… I’ll leave it to others to say whether they think my expectation was realistic or unrealistic. I tend to think 7 months really should be enough time for an uncontroversial commit to be promoted into a release but perhaps there are other inscrutable factors at play.

In the meantime,

	\type, Pif(
		Pbinop('==', Pkey(\index) mod: 3, 0) || Pbinop('==', Pkey(\index) mod: 5, 0),
		\rest,
		\note
	),

hjh

Thanks for the clarifications and the Pbinop alternative

Dangit…one more I can’t find either. I must be blind though. I spent an hour looking for it…

What’s the asterisk before a number? i.e. (*2)

Like in this code I’m studying:

pd = {|...a|PulseDivider.ar(*a)}, q = {|n,s=0,p=0| n=n+l; pd.(i, 2**n, s*2.pow(n-(p?0)-1)) }, // quantizer e = {|p,d,a|EnvGen.ar(Env.perc(a?0.01,d?0.5),p)}, // perc envelope s = {|...a|Stepper.ar(*a)},

There’s an asterisk before the “a” variable…and that’s another thing! where the hell does “a” get it’s value!!! Ugh…I feel like there are lot’s of great beginner tutorials, but I don’t see this kind of stuff explained anywhere. Gives me a reason to reach out to community though :slight_smile:

  • before an Array unpacks it to use the elements as arguments.
~addem = { | a b c | a + b + c }; //function with three arguments
~myArray = [2, 3, 100];
~addem.(2, 3, 100); // 105
~addem.(*~myArray) // 105

in your example |…a| packs the arguments into an array and stores it in a - *a unpacks them again (this is a common pattern)

SO…
Please correct me if I’m wrong.

pd is a PulseDivider.ar with arguments represented by “a”, that is an Array. “a” is given value when this thing happens?:
pd.(i, 2**n, s*2.pow(n-(p?0)-1))

I guess I just don’t understand where these arguments are coming from. For example, PulseDivider has trig div and start:
PulseDivider.ar(trig: 0.0, div: 2.0, start: 0.0)
but when you say:
pd = {|...a|PulseDivider.ar(*a)}
you are replacing “trig” with an Array called “a”, since trig is PulseDivider’s first argument??

I am probably missing something super obvious, but I feel like this is a major stepping stone that’s been bugging the hell out of me for weeks now. Any advice, answers, resources are very much appreciated

the code you are quoting just uses |…a| → *a as a shortcut - instead of specifying the arguments individually. The function pd is just forwarding its arguments to PulseDivider by packaging them into an Array.

we could rewrite pd like this:

pd = { |trig div start| PulseDivider.ar(trig, div, start) }

and it would behave the same (though with this more explicit version we could use named arguments so its a little better!)

but really I can’t see any need for the pd function, why not

q = {|n,s=0,p=0| n=n+l; PulseDivider.ar(i, 2**n, s*2.pow(n-(p?0)-1)))}

The * unpack syntax is rare… I believe it’s near the top of the dictionary help file… perhaps for Dictionary.with ?

It’s used only way: It unpacks an array, and translates it to (, , ,) syntax.

e.g. when you’re creating a collection using the .with method, you use:

Array.with(5, 6, 7, 8, 9)

… and so, if you have array [1, 2, 3], and if you were using .with, if you:

Array.with( [1, 2, 3] ) // you get [ [1, 2, 3] ]

the * operator, when used in front of a collection this way, will unpack each element, comma by comma:

Array.with(* ~my_array) // unpacks them each one by one so you get [1, 2, 3 ]

and remember, the |…| syntax will automatically collect all arguments into an array… and so if you feed it an array, you’re going to have nested arrays, which may not be what you want… and so this is when the * [ ] syntax may be useful

Tbh, if you’re a relative beginner, I wouldn’t recommend studying this kind of code.

Code can be written to be readable, or maximally short, but usually not both at the same time. If you’re still learning the basics, readability should be the priority.

If you take a beginning French course and the instructor assigns Derrida, you’d say it’s an awful course and walk away.

Here – as semiquaver says, “but really I can’t see any need for the pd function.” In terms of program logic, indeed, there’s no need for it here. It appears to be defining a shorter alias for PulseDivider, which may be useful in sctweets but… in a tweet, you wouldn’t use pd because -1 character for the name and -4 characters for var , and you’d leave out spaces wherever possible too, so clearly this isn’t a tweet… in which case there’s no need to be obscurantist.

Writing unnecessarily obscurantist code may be amusing (just as Derrida may be amusing), but it isn’t good for communication. I find, the longer I do this, that communication is more important than showing off tricks. It borders on pedantic when I write Array.fill(n, { something }) instead of { something } ! n or n.collect { something }, but with Array.fill, you know instantly exactly what to look up in the help. Communication: of several ways to do something, choose the alternative that will present the fewest obstacles to a beginner understanding the code.

At risk of stating a forehead-slapper, the primary meaning of * in SC is as a binary operator for multiplication.

  • Primary construction with *: a * b where both a and b are any expression.

  • Secondary construction: method_selector(argument, list, *expandingAnArray) where the *expanded item could even be first.

The difference between them is that the secondary use has no expression before the *.

The bit in your question is s*2, and not a standalone *2.

Here’s another case where the code is pointlessly difficult. ** and pow both mean exponentiation – why write 2**n and immediately after, 2.pow(...)? Also, in s*2.pow(...), in fact the pow will evaluate first (dot-syntax takes precedence over binary operators, IIRC) but at first glance, you’re likely to think this means (s*2).pow... (I’m pretty sure it doesn’t).

  • Terry Jones: “I came in here for an argument.”
  • John Cleese: “No, you came in here for an argument.”

Good television. Bad programming.

hjh

PS Corrected above, I remembered later that Graham Chapman was in the abuse office :grin:

Ok, it’s starting to make sense… @jamshark70 I was talking about the (*a) in the PulseDivider, But I think I understand the array unpacking now. @Rainer thanks! Makes perfect sense now…

I’m going to stop learning from this kind of code. The only reason I do, is I find something I think sounds really great, and I’m trying to dissect it.

Good analogy with Derrida haha…or German : Hegel

That’s a good reason.

If you could post the whole example, or a link, I could try to translate into readable style (if it’s not too long).

hjh

1 Like