🎄 10/25. Reduction operator in Perl 6

Welcome to Day 10 of the Perl 6 One-Liner Advent Calendar! Today, there will be three one-liners instead of a regular one.

Our today’s guest is a reduction construction with a pair of square brackets. When they do not surround an array index, they work in a completely different field.

Example 1

The most classical example, which also got to the Perl 6 Calendar 2019, is using the reduction operator to calculate factorial:

say [*] 1..2019

[ ] in Perl 6 is a reduction meta-operator. The meta part of the name tells us that it can be used as an envelope for another operator (and not only operator, by the way).

In the first example, the operator includes another operator, and the whole line can be re-written by enrolling the range to a list and placing the * between all its elements:

say 1 * 2 * 3 #`(more elements) * 2018 * 2019

Example 2

Now, let us solve problem 5 of Project Euler, where we need to find the smallest number, which is dividable by all numbers from 1 to 20.

Let me show you a direct answer in Perl 6:

say [lcm] 1..20

This code looks very similar to the previous example, but uses another operator, the lcm routine, which is an infix operator in Perl 6. The name stands for least common multiplier, but in the documentation you can also read that it returns the smallest integer that is evenly divisible by both arguments. Almost the same words, which were used to formulate the problem we solve.

say 1 lcm 2 lcm 3 lcm 4 lcm 5 lcm 6 lcm 7 # ... and up to 20

Example 3

Other infix operators that are already built-in in Perl 6, can also be very productive. Here’s an example of rotating a matrix with just a few characters of code:

[Z] <A B C>, <D E F>, <H I J>

Here, we are transforming a two-dimentional matrix with nine elements, A through J. In the output, the rows become columns, and columns become rows:

((A D H) (B E I) (C F J))

The zip infix operator Z has been inserted between the elements of the list, and thus the code is similar to the following one:

<A B C> Z <D E F> Z <H I J>

Notice, that if you want to emphasise the order of operations, you might get not exactly what you wanted:

> (<A B C> Z <D E F>) Z <H I J>
(((A D) H) ((B E) I) ((C F) J))

OK, before we did not go too far towards Lisp, let’s stop for today. See you tomorrow!

🎄 9/25. More on X, .., and … in Perl 6

Welcome to Day 9 of the Perl 6 One-Liner Advent Calendar! On Day 6, we had a construct with a cross-operator, (999...100) X* (999...100). Today, we’ll dive into a similar construction from November:

1..10 X* 1..10

It prints the items of the product table for the numbers from 1 to 10:

(1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 4 8 12 16 20 24 28 32 36 40 5 10 15 20 25 30 35 40 45 50 6 12 18 24 30 36 42 48 54 60 7 14 21 28 35 42 49 56 63 70 8 16 24 32 40 48 56 64 72 80 9 18 27 36 45 54 63 72 81 90 10 20 30 40 50 60 70 80 90 100)

There are a few things to learn about such constructions.

You may have noticed two subtle differences between 999...100 and 1..10. In one case, there are three dots, in another—only two. In the first case, the boundaries are decreasing numbers, in the second—increasing.

Two dots in Perl 6 is the range operator. It creates a Range, or, in other words, an object of the Range type.

Three dots in Perl 6 are the sequence operator. In our example, it creates a sequence, or an object of the Seq type.

You can always check the type by calling the WHAT method on the object (a fragment of the REPL session is shown below):

> (1..10).WHAT
(Range)
> (1...10).WHAT
(Seq)

It is also interesting to look into the source codes of Rakudo, and see which attributes the Range and Seq have.

my class Range is Cool does Iterable does Positional {
has $.min;
has $.max;
has int $!excludes-min;
has int $!excludes-max;
has int $!infinite;
has int $!is-int;
. . .
}
my class Seq is Cool does Iterable does Sequence {
has Iterator $!iter;
. . .
}

In the Range object, there are minimum and maximum values, while in Seq we see an iterator.

In some cases, for example, in a simple loop, you can choose between either a range or a sequence:

.say for 1..10;
.say for 1...10;

But if you want to count downwards, ranges will not work:

> .say for 10..1;
Nil

With a sequence, there’s no problem:

> .print for 10...1;
10987654321>

It is wise to play with ranges and sequences before you start feeling them well. Save them in variables, use in operations, print using say or dd.

As a starting point, consider three operations:

(1, 2, 3) X* (4, 5, 6);
(1..3) X* (4..6);
(1...3) X* (4...6);

And that’s what you can be doing until tomorrow’s Advent post!

🎄 8/25. Adding up even Fibonacci numbers in Perl 6

Welcome to Day 8 of this year’s Perl 6 One-Liner Advent Calendar. It is about ¼ of the whole series, and don’t forget that you can type ¼ instead of 0.25 in Perl 6!

Today, we are solving problem 2 from Project Euler. The task is to find the sum of all even Fibonacci numbers below four million.

Here’s the complete solution:

(1, 1, * + * ... * > 4_000_000).grep(* %% 2).sum.say

It is equally interesting to parse the code from either left or right. Let’s start from left.

Inside the first parentheses, we are generating a sequence of Fibonacci numbers, which starts with two 1s, and each following number is a sum of two previous ones. In Perl 6, you can express it using a WhateverCode block: * + * is equivalent to {$^a + $^b}.

A less known feature of Perl 6 sequences is the final condition. In many examples you would see either a bare star or an Inf. In our example, we limit the sequence with an explicit upper boundary.

Notice that you cannot simply write:

1, 1, * + * ... 4_000_000

To visualise it better, try a smaller limit, say 100:

> (1, 1, * + * ... 100)
(1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181

Perl 6 does not stop when the sequence crosses our desired border, and continues generating numbers. It will only stop it if the next calculated element of the sequence equals to the given number exactly, for example:

> (1, 1, * + * ... 144)
(1 1 2 3 5 8 13 21 34 55 89 144)

If you don’t know a Fibonacci number preceding four million, use another WhateverCode block with a Boolean condition: * > 4_000_000. Notice that the condition is opposite to what you would write in a regular loop, as here we are demanding more than four million, not less than. This is the condition that becomes True when you have to stop the sequence. Without the star, you could use the default variable: {$_ > 4_000_000}.

The rest of the code greps even numbers and adds them up. See Day 2 for more detailed explanations of these operations.

Sounds great! Let’s talk more about cool Perl 6 one-liners tomorrow.

🎄 7/25. The joy of Unicode in Perl 6

Welcome to Day 7 of the Perl 6 One-Liner Advent Calendar! Today, we’ll look at the month of March in the Perl 6 Calendar 2019:

The code here is using three characters outside of the ASCII land. We can even add one more:

say π × $𝜌²

In Perl 6, you can freely use Unicode characters in identifiers, such as variable or function names. But on top of that, there are many pre-defined symbols such as π, which have ASCII alternatives. Examine the documentation page Unicode versus ASCII symbols to see the whole set of Unicode characters that can be used in Perl 6.

Using ASCII, the above one-liner can be re-written in the following way:

say pi * $r ** 2

Let’s return to the bonus code from Day 2 and see where Unicode characters could be used:

sub f($n) {
($n <<*>> (1...1000 / $n)).grep: * < 1000
}

say (f(3) ∪ f(5)).keys.sum;

There are a few opportunities here.

First, a hyper-operator <<*>> can be replaced with proper French quotes: «*», and the multiplication character can be a cross that we used today already: «×». The same can be applied to division: ÷.

Second, the three dots of the sequence operator are replaceable with a single Unicode character:  (if you are programming in Word, you get this character automatically after typing the full stop three times).

Finally, in the last line, a Unicode character is used to find the intersection of two sets. The character here is the same that you use in mathematics (do you?), but you can use its ASCII version instead: f(3) (|) f(5).

So, we’ve got a great Christmas gift, the best Unicode support among programming languages. Use it with care not to make other people crazy about your code! See you tomorrow!

🎄 6/25. Testing palindromic numbers in Perl 6

Welcome to Day 6 of the Perl 6 One-Liner Advent Calendar! As promised yesterday, today we’ll be solving problem 4 of Project Euler. Let me once again remind you that you can pause reading and solve the problem yourself first. My intention is to demonstrate the beauty of Perl 6 and Perl in general.

So, the task is to find the largest palindromic number (the number that reads from both ends, such as 1551), which is a product of two three-digit numbers.

In other words, we have to scan the numbers below 999×999, and could optimise the solution, but in reality, we only have to allow numbers, which are products, thus, let’s not skip the multiplication part.

Here’s our today’s one-liner:

(((999...100) X* (999...100)).grep: {$^a eq $^a.flip}).max.say

If you are reading this advent calendar, then you are already prepared to the fact that chained method calls are very handy for using in Perl 6 one-liners.

We also saw the colon-form of grep earlier in Day 2, but this time we are using a code block with a placeholder variable. It is not quite clear if you can use a star here, as we need the variable twice in the block.

The first part of the line uses the cross operator X*, we’ll return to it in a few days here in this series. It generates products of all three-digit numbers. As we need the largest number, it makes sense to start from right to left, that’s why the sequence 999...100, but not 100...999.

Let’s look at the first few numbers in the grepped sequence of products:

580085 514415 906609 119911 282282 141141 853358 650056

One-liners are not always very optimal. In our case, we need to generate the whole sequence of products to find the maximum among them. The answer resides at the third position, so it will be a mistake to replace max with first. But the good part is that if you use first, Perl 6 will not generate all the numbers. There’s another useful method, head, which also prevents generating more than necessary.

The following code runs much faster and gives the correct result:

(((999...100) X* (999...100)).grep:
{$^a eq $^a.flip}).head(10).max.say

And let’s stop here for today. Stay tuned!

🎄 5/25. What’s the date today in Perl 6?

Welcome to Day 5 of this lovely Perl 6 One-Liner Advent Calendar!

Today, we’ll answer the question of what’s the date today (and tomorrow we can talk about palindromes if you want).

So, to print the answer, you can use the following line of Perl 6 code:

DateTime.now.yyyy-mm-dd.say

It looks transparent and prints the date in the format of YYYY-MM-DD. The good part is that the DateTime class is available straight ahead, and you don’t need to include a module as you do in Perl 5, for example.

$ perl6 -e'DateTime.now.yyyy-mm-dd.say'
2018-12-05

As you have already seen in the previous days of the calendar, chaining method calls is a typical Perl 6 thing. An opposite alternative would be to use say as a subroutine and use parentheses to mark method calls:

say(DateTime.now().yyyy-mm-dd());

This code also works; it is completely correct, but it looks heavy.

What you should also notice and tell your friends, is that in Perl 6, you can use dashes and apostrophes in identifiers.

Well, maybe using apostrophes is not a great idea, but hyphens are used very widely already in the source code of Perl 6. Just make sure you put spaces around minus operator in expressions to avoid any conflicts in parsing.

Exploring the sources or reading the documentation, you will find another method named in the same manner: hh-mm-ss. I bet you understand what it does.

> DateTime.now.hh-mm-ss.say
00:12:01

Notice that you will not find similar methods for different formats of the output, such as dd-mm-yy or hh-mm. Use formatter instead. It is not a method but an attribute defined in the Datish role. There is a default formatter in the DateTime class, but you may redefine it by providing the constructor with your own subroutine, for example:

DateTime.now(formatter => -> $dt {
sprintf '%02d.%02d.%04d',
$dt.day, $dt.month, $dt.year
}).say

A formatter here takes an anonymous subroutine (introduces by an arrow) with one argument $dt.

I hope this code prints the same date as our initial one-liner, as you most likely read the whole article within one day.

Nevertheless, see you tomorrow with a code that tests palindromes! 

🎄 4/25. Working with big numbers in Perl 6

Welcome to Day 4 of the Perl 6 One-Liner Advent Calendar!

Today, we’ll look at the Problem 13 of Project Euler. Let me show a screenshot of it:

Indeed, it looks huge, and the task is to find the first ten digits of the sum of a hundred integers, each consisting of 50 digits.

Sounds like a task that may require some optimisation and simplification to get rid of everything which does not contribute to the first ten digits of the result. But not in Perl 6.

In Perl 6, you can simply add up the numbers and take the first ten digits of it:

<
37107287433902102798797998220837590246510135740250
# Other 98 numbers here
53503534526472524250874054075591789781264330331690
>.sum.substr(0, 10).say

Perl 6 is operating with arbitrary-long integers by default; you don’t need to include any modules or somehow else activate this behaviour. You can even calculate powers and get the result quickly enough:

$ perl6 -e'say 37107287433902102798797998220837590 ** 1000'

Another thing to notice is that we can transparently cast strings to numbers and vice versa. In our today’s program, the list of numbers is presented as a quoted list of strings within a pair of angle brackets.

On the list, you call the sum method, which works with numbers. After getting the sum, you treat it as a string again and extract the first ten characters of it. The whole code looks very natural and easy to read.

And with this mood, we’ll say ‘Goodbye’ till tomorrow!