How to iterate a sequence of integers in decreasing order?

Viewed 132

I'd like to iterate a sequence of integers in decreasing order but I find no built-in way to do that. I'm doing this in the context of array indexes but the concept is of course general.

Ballerina has a range expression but that can be used only to iterate incrementally:

The result of a range-expr is a new object belonging to the object type Iterable<int,()> that will iterate over a sequence of integers in increasing order [...]

So this works as expected:

public function main() {
    final string[] x = ["A","B","C","D"];

    foreach int i in 0 ..< x.length() {
        io:print(x[i]);
    }
    io:println();
}
// prints: ABCD

But the following doesn't (i.e. it prints nothing) as x.length()-1 is greater than 0 (it compiles without problems though):

public function main() {
    final string[] x = ["A","B","C","D"];

    foreach int i in x.length()-1 ... 0 {
        io:print(x[i]);
    }
    io:println();
}

My current workround is to generate the indexes and consume them in two different steps, e.g.:

public function main() {
    final string[] x = ["A","B","C","D"];

    final int[] indexes = from int i in 0 ..< x.length() order by i descending select i;
    foreach int i in indexes {
        io:print(x[i]);
    }
    io:println();
}
// prints: DCBA

But this feels clumsy compared to range expression. We also loose the (supposed) laziness of the range expression, right?

The documentation says the return type of range expression is:

a new object belonging to the object type Iterable<int,()>

This and the documentation about Iterable hints that one might implement such an object but I fail to see how.

Or is there any other ways to iterate range/sequence of integers in decreasing order?

I'm using:

$ bal version
Ballerina 2201.1.0 (Swan Lake Update 1)
Language specification 2022R2
Update Tool 1.3.9
2 Answers

As of now, there's no direct way to achieve this. But it's in the plans. There's an spec issue to track this.

As an alternative, you can write an iterator (an implementation of object:Iterable interface). But it may look like a lengthy approach. Following example decrements from 20 to 10.

import ballerina/io;

public class Decrementor {
    private int startVal;
    private int endVal;
    private int i;

    public function init(int startVal, int endVal) {
        self.startVal = startVal;
        self.endVal = endVal;
        self.i = startVal;
    }

    public function next() returns record {|int value;|}? {
        if self.i < self.endVal {
            return ();
        }

        int val = self.i;
        self.i -= 1;
        return {value: val};
    }
}

class Iterator {
    *object:Iterable;

    public function iterator() returns Decrementor {
        return new Decrementor(20, 10);
    }
}

public function main() {
    foreach any|error i in new Iterator() {
        io:println(i);
    }
}
import ballerina/io;
public function main() {
    final string[] x = ["A","B","C","D"];

    // Method 1
    foreach string i in x.reverse() {
        io:println(i); // DCBA
    } 
    io:println(x); // ["A","B","C","D"]
    
    // Method 2
    foreach int i in -(x.length()-1) ... 0 {
        io:println(x[-i]); // DCBA
    }
}
Related