Array will not reverse

Simple - I hope - question.

I have this code:

a = Pgeom(1, 2, 12).reciprocal; a.asStream.nextN(12);

Which produces this on the server:

[ 1.0, 0.5, 0.25, 0.125, 0.0625, 0.03125, 0.015625, 0.0078125, 0.00390625, 0.001953125, 0.0009765625, 0.00048828125 ]

I want to reverse it so I use this code:

b = a.reverse;

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.

Thank you!

You need to re-assign a in the second statement:

(
a = Pgeom(1, 2, 12).reciprocal; 
a = a.asStream.nextN(12);
b = a.reverse;
)

Best,
Paul

Thank you so much Paul, greatly appreciated! Problem solved.

I perhaps spoke too soon.

By having this…

(
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.

[ 0.00048828125, 0.0009765625, 0.001953125, 0.00390625, 0.0078125, 0.015625, 0.03125, 0.0625, 0.125, 0.25, 0.5, 1.0 ]

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).

In other words, try this instead:

\end, Pseq(b*~b0.numFrames, 1),

Thanks shiihs,

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.

Thanks again for your help :slight_smile:

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 :slight_smile:

Imagine a big table with a lot of stuff on it.

This is SC’s memory space.

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.

hjh

…some aspects are still confusing.

i.e.

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;

It’s documented though – for Signal:

reverse(beginSamp: 0, endSamp)

Reverse a subrange of the Signal in place.

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.

hjh

=> {|a| ^a.this}

Do you perhaps mean:

=> {|a| ^a.value(this) }

?

Nice!

If you give it a non operator name, say “apply”, you can happily tack more messages at the end?

“xy”.reverse.apply({ arg i; i.reverse ++ i }).reverse == “xyyx”

This kind of sequencing is very nice but it does mean that “for effect” messages can’t answer anything other than “themselves” or you’re stuck.

Ps. Smalltalk (as I’m sure you know) has “message cascades” as well, to sequence “for effect” messages regardless of how they answer.

It’s a nice thing, I think, but only Smalltalk people seem to like it!

(Actually, maybe Sc has it too, I don’t know!?)

1 Like