Welcome to Day 15 of the Perl 6 One-Liner Advent Calendar! Today, there will be two one-liners, and they both generate Fibonacci numbers.
Yes, most likely, you never used such numbers in real code, and, again, most likely, you solved many educating problems with them. Nevertheless, today, let’s solve the Problem 25 of the Project Euler and try to approach the shortest solution for a Fibonacci problem on the Code-Golf.io site.
Pre-party
How do we form a Fibonacci sequence in Perl 6? With a sequential operator ...:
0, 1, * + * ... *
If you want some exotic flavour in the code, you can replace the last star with either Inf or ∞. In any case, the result will be a lazy sequence of the Seq type. Perl 6 will not compute it immediately (and it can’t, as the right edge is infinite).
Part 1
The first task is to find the index of the first Fibonacci number, which has 1000 digits. Of course, you can loop over the above-created sequence and trace the index yourself. But in Perl 6, there is an option to modify the grep routine family, and ask it to return the index of the matched item instead of the item itself.
Also, instead of grep, we’ll use a more appropriate method, first. It will return the first matching item or its index if we call the method with the k key. It is enough just to mention the key, no value is really needed.
say (0, 1, * + * ... *).first(*.chars >= 1000, :k)
This program prints a single integer number, which is the correct answer to the given problem.
Part 2
Now let’s solve a Golf task and print the first 30 Fibonacci numbers, one on a line. This time, we have to use as few characters in the code as possible.
The first approach is rather wordy (even with ^31 instead of 0..30 it needs 33 characters):
.say for (0, 1, * + * ... *)[^31]
There is some room that allows compression. Of course, the first and the most obvious thing is to remove spaces (remaining 28 characters):
.say for (0,1,*+*...*)[^31]
Another interesting trick is to use the >> meta-operator to call the say method on each element of the sequence. It compresses the code further to 24 characters:
(0,1,*+*...*)[^31]>>.say
At this moment, we can employ some Unicode, and gain three more characters (21 left):
(0,1,*+*…*)[^31]».say
Looks compact already, but there are still some options to try. Let us get rid of the explicit slicing, and try to stop the sequence at the last element:
(0,1,*+*…*>514229)».say
The code became longer (23 characters), but we do not need an exact number 514229 here. It is enough to give some number, which is bigger then the 29th and smaller then the 30th element of the sequence. For example, it can be 823543, which is 7 powered 7. Write it down using superscripts (19 characters):
(0,1,*+*…*>7⁷)».say
Finally, it is possible to make it one character less by using another Unicode character representing 800000 in a single character. Not every (if any) font can display something visually attractive, but Perl 6 takes the character with no complains:
(0,1,*+*…*>𐧴)».say
These 18 characters is one character longer than the top result at Gode-Golf. I have a feeling that you could gain another character by replacing the first two elements of the sequence with ^2, but that does not work in current Rakudo, and you have to return a character to flatten the list: |^2, which makes the solution 18 characters long again.
The desire is to remove the *> part in the condition to stop the sequence and replace it with a fixed number. Unfortunately, there’s no way to express 832040 with powers of the numbers between 1 and 90. Would that be possible, we could use a superscript to calculate the number.
Another idea is to use a regex, but we need at least four characters, which does not help:
(0,1,*+*…/20/)».say
And let me stop here for today. Would you happen to create a shorter solution, please leave a comment. See you tomorrow!
It is not a good idea to use `>>` for side-effects, as `say`, as the **order** in which `>>` is allowed to execute, is **not** defined. So you **will** get all numbers, but there’s no guarantee you will get them in the right order. The `Seq` that is a result of `>>` **Is** guaranteed to be in the right order, though.
Isn’t `>>` defined in terms of `.hyper`?
Technically `»` is called a “hyperoperator”, here modifying the method call operator `.`. I don’t know if it’s implemented in terms of .hyper, but the similar names would lead me to expect the same order-preserving behavior of .hyper, rather than the scrambling allowed by .race.
FWIW, replacing >> with » may not technically help in code golf challenges, depending on the rules; many are counted in encoded bytes rather than characters, and non-ASCII chars are all at least two bytes in UTF-8. Replacing three periods with the single character … does save one byte, but the superscript ⁷ takes three, which is no shorter than the boring ASCII **7.