๐Ÿ”ฌ68. The smartness of the sequence operator in Perl 6, part 1

In Perl 6, you can ask the sequence operator to build a desired sequence for you. It can be arithmetic or geometric progression. All you need is to show the beginning of the sequence to Perl, for example:

.say for 3, 5 ... 11;

This prints numbers 3, 5, 7, 9, and 11. Or:

.say for 2, 4, 8 ... 64;

This code prints powers of 2 from 2 to 64: 2, 4, 8, 16, 32, and 64.

I am going to try understanding how that works in Rakudo. First of all, look into the src/core/operators.pm file, which keeps a lot of different operators, including a few versions of the ... operator. The one we need looks really simple:

multi sub infix:<...>(\a, Mu \b) {
    Seq.new(SEQUENCE(a, b).iterator)
}

Now, the main work is done inside the SEQUENCEย sub. Before we dive there, it is important to understand what its arguments a and b receive.

In the case of, say, 3, 5 ... 11, the first argument is a list 3, 5, and the second argument is a single valueย 11.

These values land in the parameters of the routine:

sub SEQUENCE(\left, Mu \right, :$exclude_end) {
    . . .
}

What happens next is not that easy to grasp. Here is a screenshot of the complete function:

sequence

It contains about 350 lines of code and includes a couple of functions. Nevertheless, letโ€™s try.

What you see first, is creating iterators for both left and right operands:

my \righti := (nqp::iscont(right) ?? right !! [right]).iterator;
my \lefti := left.iterator;

Then, the code loops over the left operand and builds an array @tail out of its data:

while !((my \value := lefti.pull-one) =:= IterationEnd) {
    $looped = True;
    if nqp::istype(value,Code) { $code = value; last }
    if $end_code_arity != 0 {
        @end_tail.push(value);
        if +@end_tail >= $end_code_arity {
            @end_tail.shift xx (@end_tail.elems - $end_code_arity)
                unless $end_code_arity ~~ -Inf;

            if $endpoint(|@end_tail) {
                $stop = 1;
                @tail.push(value) unless $exclude_end;
                last;
            }
        }
    }
    elsif value ~~ $endpoint {
        $stop = 1;
        @tail.push(value) unless $exclude_end;
        last;
    }
    @tail.push(value);
}

I leave you reading and understand this piece of code as an exercise, but for the given example, the @tailย array will just contain two values: 3 and 5.

> .say for 3,5...11;
multi sub infix:<...>(\a, Mu \b)
List    # nqp::say(a.^name);
~~3     # nqp::say('~~' ~ value);
~~5     # nqp::say('~~' ~ value);
elems=2 # nqp::say('elems='~@tail.elems);
0=3     # nqp::say('0='~@tail[0]);
1=5     # nqp::say('1='~@tail[1]);

This output shows some debug data print outs that I added to the source code to see how it works. The green comments show the corresponding print instructions.

Thatโ€™s it for today. See you tomorrow with more stuff from the sequence operator. Tomorrow, we have to understand how the listย 3, 5ย tells Perl 6 to generate increasing values with step 1.

One thought on “๐Ÿ”ฌ68. The smartness of the sequence operator in Perl 6, part 1”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s