Something cool about Perl 6 every day

Primary Menu

Day 23 – The Wonders of Perl 6 Golf

Ah, Christmas! What could possibly be better than sitting around the table with your friends and family and playing code golf! … Wait, what?

Oh, right, it’s not Christmas yet. But you probably want to prepare yourself for it anyway!

If you haven’t noticed already, there’s a great website for playing code golf: https://code-golf.io/. The cool thing about it is that it’s not just for perl 6! At the time of writing, 6 other langs are supported. Hmmm…

Anyway, as I’ve got some nice scores there, I thought I’d share some of the nicest bits from my solutions. All the trickety-hackety, unicode-cheatery and mind-blowety. While we are at it, maybe we’ll even see that perl 6 is quite concise and readable even in code golf. That is, if you have a hard time putting your Christmas wishes on a card, maybe a line of perl 6 code will do.

I won’t give full solutions to not spoil your Christmas fun, but I’ll give enough hints for you to come up with competitive solutions.

All I want for Christmas is for you to have some fun. So get yourself rakudo to make sure you can follow along. Later we’ll have some pumpkin pie and we’ll do some caroling. If you have any problems running perl 6, perhaps join #perl6 channel on freenode to get some help. That being said, https://code-golf.io/ itself gives you a nice editor to write and eval your code, so there should be no problem.

Some basic examples

Let’s take Pascal’s Triangle task as an example. I hear ya, I hear! Math before Christmas, that’s cruel. Cruel, but necessary.

There’s just one basic trick you have to know. If you take any row from the Pascal’s Triangle, shift it by one element and zip-sum the result with the original row, you’ll get the next row!

So if you had a row like:

1331

All you do is just shift it to the right:

01331

And sum it with the original row:

1331

++++

01331

=

14641

As simple as that! So let’s write that in code:

for^16 { put (+combinations($^row,$_) for0..$row) }

You see! Easy!

… oh… Wait, that’s a completely different solution. OK, let’s see:

.putfor1, { |$_,0Z+0,|$_ } … 16

Output:

1

11

121

1331

14641

15101051

1615201561

172135352171

18285670562881

193684126126843691

1104512021025221012045101

1115516533046246233016555111

1126622049579292479249522066121

11378286715128717161716128771528678131

11491364100120023003343230032002100136491141

11510545513653003500564356435500530031365455105151

Ah-ha! There we go. So what happened there? Well, in perl 6 you can create sequences with a very simple syntax: 2, 4, 8 … ∞. Normally you’ll let it figure out the sequence by itself, but you can also provide a code block to calculate the values. This is awesome! In other languages you’d often need to have a loop with a state variable, and here it does all that for you! This feature alone probably needs an article or 𝍪.

The rest is just a for loop and a put call. The only trick here is to understand that it is working with lists, so when you specify the endpoint for the sequence, it is actually checking for the number of elements. Also, you need to flatten the list with |.

If you remove whitespace and apply all tricks mentioned in this article, this should get you to 26 characters. That’s rather competitive.

Similarly, other tasks often have rather straightforward solutions. For example, for Evil Numbers you can write something like this:

.base(2).comb(~1) %%2&&.sayfor^50

Remove some whitespace, apply some tricks, and you’ll be almost there.

Basically, almost all perl 6 solutions look like real code. It’s the extra -1 character oomph that demands extra eye pain, but you didn’t come here to listen about conciseness, right? It’s time to get dirty.

Numbers

Let’s talk numbers! 1 ² ③ ٤ ⅴ ߆… *cough*. You see, in perl 6 any numeric character (that has a corresponding numeric value property) can be used in the source code. The feature was intended to allow us to have some goodies like ½ and other neat things, but this means that instead of writing 50 you can write ㊿. Some golfing platforms will count the number of bytes when encoded in UTF-8, so it may seem like you’re not winning anything. But what about 1000000000000 and 𖭡? In any case, code-golf.io is unicode-aware, so the length of any of these characters will be 1.

So you may wonder, which numbers can you write in that manner? There you go:

-0.50.006250.0250.03750.050.06250.0833330.1

0.1111110.1250.1428570.150.1666670.18750.2

0.250.3333330.3750.40.4166670.50.5833330.6

0.6250.6666670.750.80.8333330.8750.9166671

1.522.533.544.555.566.577.588.5910

1112131415161718192021222324252627

2829303132333435363738394041424344

45464748495060708090100200300400500

6007008009001000200030004000500060007000

8000900010000200003000040000500006000070000

8000090000100000200000216000300000400000

4320005000006000007000008000009000001000000

100000000100000000001000000000000

This means, for example, that in some cases you can save 1 character when you need to negate the result. There are many ways you can use this, and I’ll only mention one particular case. The rest you figure out yourself, as well as how to find the actual character that can be used for any particular value (hint: loop all 0x10FFFF characters and check their .univals).

For example, when golfing you want to get rid of unnecessary whitespace, so maybe you’ll want to write something like:

say5max3 # ERROR

It does not work, of course, and we can’t really blame the compiler for not untangling that mess. However, check this out:

say ⑤max③ # OUTPUT: «5␤»

Woohoo! This will work in many other cases.

Conditionals

If there is a good golfing language, that’s not Perl 6. I mean, just look at this:

puts 10<30?1:2# ruby

say10<30??1!!2 # perl 6

Not only TWO more characters are needed for the ternary, but also some obligatory whitespace around < operator! What’s wrong with them, right? How dare they design a language with no code golf in mind⁉

Well, there are some ways we can work around it. One of them is operator chaining. For example:

say5>3>say(42)

If 5 is ≤ than 3, then there’s no need to do the other comparison, so it won’t run it. This way we can save at least one character. On a slightly related note, remember that junctions may also come in handy:

say‘yes!’if5==3|5

And of course, don’t forget about unicode operators: ≥, ≤, ≠.

Typing is hard, let’s use some of the predefined strings!

You wouldn’t believe how useful this is sometimes. Want to print the names of all chess pieces? OK:

say (‘♔’…‘♙’)».uniname».words»[2]

# KING QUEEN ROOK BISHOP KNIGHT PAWN

This saves just a few characters, but there are cases when it can halve the size of your solution. But don’t stop there, think of error messages, method names, etc. What else can you salvage?

Base 16? Base 36? Nah, Base 0x10FFFF!

One of the tasks tells us to print φ to the first 1000 decimal places. Well, that’s very easy!

Hmmm… so that gives us digits for any arbitrary base. How high can we go? Well, it depends on what form we would like to store the number in. Given that code-golf.io counts codepoints, we can use base 0x10FFFF (i.e. using all available codepoints). Or, in this case we will go with base 0x10FFFE, because:

How do we reverse the operation? During one of the squashathons I found a ticket about a feature that I didn’t know about previously. Basically, the ticket says that Rakudo is doing stuff that it shouldn’t, which is of course something we will abuse next time. But for now we’re within the limits of relative sanity: