
Sequences in Perl 6 / Rakudo
- Transfer
Perl 6 introduces a new operator ... for defining sequences. Here's how it works:
Usage example:
(When defining a geometric sequence in the REPL, which is infinite by definition, I put “1;” at the end of the line. As a result, REPL outputs a unit and does not go into an infinite loop.)
To limit the infinite “lazy” sequence, in the example I used the notation [^ 10], which means "the first ten elements." With such a record, the calculated variables are stored for future use.
The sequence operator ... is a powerful tool for creating lazy lists. If he is given one number, he begins the countdown from it. If you ask two, he regards them as an arithmetic sequence. If three - he checks to see if they are the beginning of an arithmetic or geometric sequence - if so, then he continues them.
Of course, many sequences are not arithmetic or geometric. You can explicitly specify a function that defines the following number in a sequence:
The part -> $ a, $ b {$ a + $ b} is a switch block (lambda function) that takes two arguments and returns their sum. The sequence operator calculates how many arguments the block takes and passes the right amount from the end of the sequence to generate the next number.
So far, for all examples, an asterisk has been indicated at the end, meaning "anything." In this case, the list has no end. If you put a number there, it will be the end of the list.
The first list ends naturally, but the second passes by the limiter. As a result, we get an endless list - so I limited it to 14 elements, just to see what it gives out.
Programmers familiar with floating point calculations probably grumble that one cannot assume that adding 0.1 to a number inevitably leads to a deuce. Perl 6 uses Rat math, which provides the accuracy and performance of such calculations.
If I had to find all Fibonacci numbers less than 10,000, it would be difficult for me to calculate which number will be the last in the sequence. Fortunately, in addition to the block for setting the sequence formula, we can use the block for the boundary condition.
Arrow block -> $ a {$ a> 10000} creates a check. It takes one argument and returns true when it becomes more than 10000.
However, we wanted to get all the numbers less than 10000. And we got one superfluous. For our task, there is an alternative notation for the limiter block, which indicates that the last number should not be included in the sequence:
Using the closures of “anything”, this record can be redone as follows:
Such a record is clearer or more difficult - it's up to you.
In addition, any list, including the lazy one, can be found on the left side of the sequence. That is, you can use the bounding block to get the limited part of the existing lazy list:
In addition, the sequence operator is not limited to working exclusively with numbers. By setting your own function to calculate the next element of the list, you can compose it from any elements you want.
my @even-numbers := 0, 2 ... *; # арифметическая последовательность
my @odd-numbers := 1, 3 ... *;
my @powers-of-two := 1, 2, 4 ... *; # геометрическая последовательность
Usage example:
> my @powers-of-two := 1, 2, 4 ... *; 1;
1
> @powers-of-two[^10]
1 2 4 8 16 32 64 128 256 512
(When defining a geometric sequence in the REPL, which is infinite by definition, I put “1;” at the end of the line. As a result, REPL outputs a unit and does not go into an infinite loop.)
To limit the infinite “lazy” sequence, in the example I used the notation [^ 10], which means "the first ten elements." With such a record, the calculated variables are stored for future use.
The sequence operator ... is a powerful tool for creating lazy lists. If he is given one number, he begins the countdown from it. If you ask two, he regards them as an arithmetic sequence. If three - he checks to see if they are the beginning of an arithmetic or geometric sequence - if so, then he continues them.
Of course, many sequences are not arithmetic or geometric. You can explicitly specify a function that defines the following number in a sequence:
> my @Fibonacci := 0, 1, -> $a, $b { $a + $b } ... *; 1;
1
> @Fibonacci[^10]
0 1 1 2 3 5 8 13 21 34
The part -> $ a, $ b {$ a + $ b} is a switch block (lambda function) that takes two arguments and returns their sum. The sequence operator calculates how many arguments the block takes and passes the right amount from the end of the sequence to generate the next number.
So far, for all examples, an asterisk has been indicated at the end, meaning "anything." In this case, the list has no end. If you put a number there, it will be the end of the list.
> 1, 1.1 ... 2
1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2
> 1, 1.1 ... 2.01
... шестерёнки Rakudo вращаются, ибо этот список бесконечен ...
> (1, 1.1 ... 2.01)[^14]
1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2 2.1 2.2 2.3
The first list ends naturally, but the second passes by the limiter. As a result, we get an endless list - so I limited it to 14 elements, just to see what it gives out.
Programmers familiar with floating point calculations probably grumble that one cannot assume that adding 0.1 to a number inevitably leads to a deuce. Perl 6 uses Rat math, which provides the accuracy and performance of such calculations.
If I had to find all Fibonacci numbers less than 10,000, it would be difficult for me to calculate which number will be the last in the sequence. Fortunately, in addition to the block for setting the sequence formula, we can use the block for the boundary condition.
> 0, 1, -> $a, $b { $a + $b } ... -> $a { $a > 10000 };
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946
Arrow block -> $ a {$ a> 10000} creates a check. It takes one argument and returns true when it becomes more than 10000.
However, we wanted to get all the numbers less than 10000. And we got one superfluous. For our task, there is an alternative notation for the limiter block, which indicates that the last number should not be included in the sequence:
> 0, 1, -> $a, $b { $a + $b } ...^ -> $a { $a > 10000 };
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765
Using the closures of “anything”, this record can be redone as follows:
> 0, 1, * + * ...^ * > 10000;
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765
Such a record is clearer or more difficult - it's up to you.
In addition, any list, including the lazy one, can be found on the left side of the sequence. That is, you can use the bounding block to get the limited part of the existing lazy list:
> my @Fibonacci := 0, 1, * + * ... *; 1;
1
> @Fibonacci ...^ * > 10000
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765
> @Fibonacci[30]
832040
In addition, the sequence operator is not limited to working exclusively with numbers. By setting your own function to calculate the next element of the list, you can compose it from any elements you want.