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.
Is it … *>4m or …^ *>4m? Or … *>=4m? Is the last element the first one for which the predicate holds? I’m looking at https://docs.perl6.org/language/operators#index-entry-…_operators and I see “the sequence is terminated when the smartmatch succeeded. For the … operator, the final element is included”. Thanks in advance for any clarification you can provide!
Very valid comment.
But I thought that there’s a very small chance 4000000 is a Fibonacci number, so we can gain one character in the one-liner 🙂 But to be strict, it should be >=.
Actually…
> (1, 1, * + * … * > 4_000_000).tail
5702887
It happens to be odd, so isn’t included in the sum, and the answer is correct. But that’s not a “very slim chance”, 33⅓% of all Fibonacci numbers is even.
Using ≥ doesn’t help either.
Correct version:
1, 1, * + * …^ 4_000_000
Your correct version is incorrect.
Oops… 😊
1, 1, * + * …^ * > 4_000_000