Cristoffel words programming in supercollider

Hello, I’m pretty new to programming and trying to recreate a pseudo code that the book Creating Rhythms of Richard and Stefan Hollos offers to obtain upper Cristoffel words for y/x slopes, where y are the note onsets and the x are silences in a total of x+y beats rhythm length. Given pseudo code is:

a = y
b = x

if(upper word) print 1 else print 0
while( a != b )
{
if ( (a > b)
{
print 1
b = b + x
}
else
{
print 0
a = a + y
})
}
if(upper word) print 0 else print 1

The code I came up for in Supercollider is (where I set x=1 and y=2):

(
{x = 1; y = 2; a = y; b = x;};
“Upper word = 1”.postln;
{
while( {a != b} ,
{
if ( (b < a), {“1”.postln; b = b + x ; b.postln}, {“0”.postln; a = a + y; a.postln} );
};
);
};
“Upper word = 0”.postln;
)

But this doesn’t work. I am having a hard time finding on the web info about straightforward programming of “while” and “if” in Supercollider.
Also I haven’t found a friendly way to allow any user to input x and y variables values (in the example, 1 and 2 repectively), so what I mean is whether supercollider could be programmed to prompt the user to provide such values.

As I said I’m pretty new to all of this and I will appreciate any guidance and support to figure out what it is what I’m doing wrong. Thanks a lot for your help. Best, JF

Hello! Welcome to SuperCollider :slight_smile:
I don’t see anything wrong in your ifs and whiles, but there’s an issue in how you write your function.

This is just a function definition. Everything between curly brackets is a Function. A function is an object which can be referenced by variables and responds to a value() method, which actually evaluates the instructions contained in the function.
So, to make it obvious, by writing the above you are not setting x=1, y=2, a=y and b=x; you are only defining a function that will apply those assignments when called.
A Function can take input values as arguments, which you can define using the keyword arg or by using pipes (| |). I’ll show you how could you write your code as a proper function which takes arguments and it’s stored in a variable, so that you can later call the same function with different arguments.

// this block defines your algorithm as a function
// round parenthesis are a way to execute the whole block at once just by putting your cursor anywhere within the parenthesis and pressing ctrl+enter
(
// variable names starting with ~ define global variables
~upperWord = {
    // arguments are defined at the beginning of the function's body
    |x=1, y=2|
   // define a and b as local variables, since you need them only inside your function
    var a = y, b = x;
    "Upper word = 1".postln;
    while { a != b } // this first function is while's condition check
    { // this second function is while's body
        if(b<a) { // if: first function is executed if condition is true
            "1".postln; b = b+x; b.postln;
        }{ // if: second block is the 'else' block
            "0".postln; a = a+y; a.postln;
        }
    }; // end of while's body
    "Upper word = 0".postln;
};
) 

// now that we defined a function as a global variable we can call it with different arguments
// execute the following lines one at a time:

~upperWord.value() // no arguments, defaults are used (x=1, y=2)

~upperWord.value(2,3) // (x=2, y=3)

// there's a shortcut for calling .value()
// this one is equivalent to the last one above:
~upperWord.(2,3)

I haven’t looked into your algorithm yet, I just wanted to point you out on some syntax fixes. Hope this gets you started.
To recap:

  • {} define a function. They don’t necessarily call the function, only define it.
  • When you pass functions to whiles and ifs, they get called for you by the control cycle
  • Otherwise, you need to explicitly call the function by using the .value() or .() method.
  • () around a block of code are only meant to facilitate code execution
  • Maybe obvious, but just for completeness: () after a method call are meant to contain argument values to pass to that call ( as in ~upperWord.value(0,1), where 0 and 1 are arguments of the .value() method.

I can also warmly suggest you to go through the Getting Started tutorial, which you find directly in your Help Browser within SuperCollider, or online if you prefer. It will walk you through all the basic knowledge you need to write SuperCollider code. And since you have already some goals in mind that you want to realize, it will be even fun and rewarding :exploding_head:

Wow! that’s great and very informative. Thank you so much for your explanations and suggestions, I’ll follow them up right away. I’ll try this first and then I’'ll try to jump into what I believe programming an interface for this code so a user can input his own values (as in the (x=2,y=3) values on your line: ~upperWord.value(2,3) ).The program seems to run just fine. It’s a great way to start!
I think now my next goal is to show the total result as a string of values or as an array. So for the program as it is (I just deleted the a.postln and b.postln of the original):

(
~upperWord = {
|x=1, y=2|
var a = y, b = x;
“Upper word = 1”.postln;
while { a != b }
{
if(b<a) {
“1”.postln; b = b+x;
}{
“0”.postln; a = a+y;
}
};
“Upper word = 0”.postln;
};
)

~upperWord.value(2,3)

I should get the Upper Chistoffel word for x=2 and y=3 (3 note onsets on a five pulses length rhythm) is: 11010 or [1,1,0,1,0]

Thanks so much for giving me the hints I was looking for. Best regards JF

One quick note about forum usage – please enclose all code fragments in backtick delimiters:

```
write your code
between triple-backticks
```

Or, for in-line code fragments, a single backtick before and after: xyz is written “backtick xyz backtick”.

Two reasons to do that:

  1. Notice that all of the code indentation is gone, above. This makes it harder to read, and harder for people to help you.

  2. “Upper word = 0”.postln; is actually invalid code – the straight quote marks have been replaced with typographic open/close quotes. This doesn’t happen if you mark the code as code.

Thanks,
hjh

BTW, using elgiano’s code above, it’s useful (I think) to know that you can write an argument two ways, either using the two lines vertical bars (pipes) as in the above code:

| a = 1, b = 2 |

Or, which I find clearer to ‘see’ in code blocks, the word arg + semi-colon:

arg a = 1, b = 1;

You’ll notice the word ‘arg’ becomes red in your supercollider code - which is what I mean by it’s more visible.

Thank you jamshark70!, certainly I was wondering why indentation was gone (and of course I got your point about open/close quotes marks “ ” being invalid). I didn’t know this trick (enclosing between triple-backticks) for the forum. From now on I’ll do it.

Thank you joesh!, for the tip about your preference for the “colored” arg way.

Cheers!, JF