But I get the error message ¨ERROR: Message ‘reverse’ not understood.¨
I think this is because the initial code is creating an array, rather than a list, but I’m not sure. At this point a generalised confusion steps in. I’ve been idly going through support documentation, but I’m not sure I’m framing my question correctly, so don’t feel I’m getting closer to an answer.
A point in the right direction would be greatly appreciated.
(
a = Pgeom(1, 2, 12).reciprocal;
a = a.asStream.nextN(12);
b = a.reverse;
)
Rather than my previous
a = Pgeom(1, 2, 12).reciprocal; a.asStream.nextN(12);
The variable works differently.
It’s being used in a Pmono as such…
\end, Pseq([b]*~b0.numFrames, 1),
When I re-assign the a the Pmono play multiple instances of the buffer simultaneously, rather than going through the array step by step as it did with my earlier code.
I can’t understand why this is because it appears identical in the server window.
that’s because the variable b is already a list, so you don’t need to wrap it in square brackets anymore when you use it in the Pseq. (A “list of list” is interpreted as a chord by the pattern system, which is why everything plays simultaneously in your case).
This makes sense, and your solution works. I still slightly mystified as to why it took the re-assignment to turn it into a list when it already appeared as a list in the server window.
The reassignment is needed because “reverse” produces a new list that contains the reversed data instead of modifying the given list directly.
it can be very instructive to add some .postln or .debug statements in your code to understand better what is happening, e.g.
(
a = [1,2,3];
a.debug("a original");
a.reverse;
a.debug("a after calling reverse");
a = a.reverse;
a.debug("a after calling reverse and reassignment");
)
Thank you, this is very helpful. I’m getting a better feel for this now I think, although some aspects are still confusing. Very clear example of yours - thanks for sharing
a = [1, 2, 3] makes an array, which lives somewhere on this table. The a = part means, a is now like an arrow that points to this array.
At any time, you can do a = something_else and a's arrow will move to point to the other thing (forgetting the previous arrow).
A lot of people get tripped up on the use of variables in expressions.
a.reverse means 1. Go find the thing at the other end of a's arrow. 2. Use this thing in place of a. That is, after resolving a to its value, it’s only operating on the array. a is gone from the expression – there is no link between the expression’s result and the identifier a.
Variables are not objects. They refer to objects, but it is always the object that does the work, never the variable. The variable is only a pointer, nothing more.
To get a to point to the new result, you need a = again.
var x = Array[1, 2, 3]; x.reverse; x[0] == 1
var x = Signal[1, 2, 3]; x.reverse; x[0] == 3
Generally with messages you have to read a little, or experiment, they can do all sorts of things!
Ps. An in place reverse can be useful too, and you can have both, i.e.
+ SequenceableCollection {
reverseInPlace {
// Reverse this collection in place
var start = 0;
var end = this.size - 1;
{ start < end }.while({
var temp = this.at(start);
this.put(start, this.at(end));
this.put(end, temp);
start = start + 1;
end = end - 1;
})
}
}
Thank you, both these responses are very helpful. The variable concept makes sense to me, although with repeated practise this programming grammar becomes more intuitive and deep seated.
var x = Array[1, 2, 3]; x.reverse; x[0] == 1
var x = Signal[1, 2, 3]; x.reverse; x[0] == 3
Okay this is a great example of confusing; it feels more like a riddle than computer logic!
I think one issue is that in a functional programming style we want to operate on return values rather than creating side effects. SC allows this by for example chaining method calls [1,2,3].reverse.postln.
serially assigning variables is a pretty cumbersome way of achieving the same thing. The alternative in SC is to use nested expressions but this is cumbersome as well.
I define => {|a| ^a.value(this)} and that allows me to compose functions without having to store the return values inbetween.
It has the annoyance that if you want to tack on another method call you need to make a curried function: [1,2,3].reverse => {|i| some function} => _.postln;
Another clue that something is up: Array:reverse takes no arguments, while Signal:reverse takes start/end sample positions to flip between. This by itself doesn’t immediately make it clear what the difference is, but it is a hint that Signal:reverse is not the same operation as Array:reverse. Digging further, you’d find that Array:reverse always reverses the entire array (and as such, very likely the original and reversed arrays would have no unchanged segments, thus it’s logical to return an all new array) while Signal:reverse can, like in Audacity, reverse just a short clip (perhaps leaving most of the Signal untouched, in which case “in place” makes more sense).
In that sense, object-oriented polymorphism is kind of like a Humpty Dumpty language: when Signal uses the word reverse, it means what Signal chooses it to mean; when Array uses it, it means what Array chooses it to mean.