Ved Thiru

Advent of Code Day 1, in Raku!

published 2024-12-01

in which I showcase cool Raku features, and make fun of their names

I’m doing Advent of Code this year. Today is Day 1! I’ll be using Raku today, previously known as Perl 61. I have explanations for my solutions below. If you’re curious about Raku, I’ve tried to showcase the cool parts of the language in my solution.

I won’t re-describe the puzzles, so take a look on the Advent of Code website.

Part 1

The puzzle is relatively straightforward: we are given two lists, and we are supposed to find the sum of absolute differences between them.

Here’s my solution:

my $file = open 'data.txt';

my (@v, @u);

for $file.lines -> $line {
  my ($x, $y) = $line.words;
  @v.append(+$x);
  @u.append(+$y);
}

say [+] (@v.sort Z- @u.sort).map: *.abs;

Let’s go through it line-by-line:

my $file = open 'data.txt';

For those of you who haven’t used Perl or Raku before, my is how you declare local variables, bound to the current scope. All variables in Perl have sigils. Sigils are used to differentiate between ‘anything’ ($), arrays (@), and hashmaps (%).

my (@v, @u);

Here we are declaring two arrays in the local scope.

for $file.lines -> $line {
  # ...
}

.lines gives us a sequence, which we then use in the block (binding each line to the block-scoped variable $line).

  my ($x, $y) = $line.words;

.words gives a list of the words (separated by whitespace) in the string. We bind the left ‘word’ to $x, and the right word to $y. $x and $y are strings! We’ll need to change this.

  @v.append(+$x);
  @u.append(+$y);

+, when used as a prefix operator, is the ’numeric context operator’ (Raku has some strange names for things). It converts anything to a number (if it can’t be converted to a number, it gives NaN). We then append this number to the corresponding array.

say [+] (@v.sort Z- @u.sort).map: *.abs;

So much syntax! Raku makes heavy use of syntax, and has tons of operators for common tasks. 2 I’ll split this explanation up into expressions. From the inside out:

  1. @v.sort: Sort is a method on Arrays. In Raku, you can leave out the parentheses in a method call if you are not calling the method with any arguments, like here.

  2. @v.sort Z- @u.sort: Z is the “zip” metaoperator 3. It takes a value from the left array and a value from the right array, and applies the operator given. Here, I’ve given the operator -, which is subtraction. So, this is doing pairwise subtraction between the two (sorted) arrays.

  3. .map: *.abs. map is a method on Arrays that takes a Block and applies it to every value in the array. The ‘fully-expanded’ version of this block would be -> $i { return $i.abs(); } 4. This can be simplified:

    • The last statement in the block will be the return value (if we don’t use return), so we can remove the return statement: -> $i { $i.abs() }.
    • We’ll also remove the parentheses as before: -> $i { $i.abs }.
    • * is the Whatever Star (have I mentioned the strange names Raku has yet?). In this situation, * can be used as an operand (to .abs), which will become a Block taking an argument. So we can write our block as *.abs. Whatever Stars are used in other situations, too.

    Together, this means “take the absolute value of each entry in the array”.

  4. [+] is a usage of the reduction metaoperator. Just like Z, [ ] takes an operator (here, +), and uses it to reduce a list. The initial accumulator is the identity for that operator, so [+] sums the list.

In total, this line sums up the absolute values of the pairwise differences of the sorted lists. say is what other languages call print.

Part 2

We can solve this puzzle by finding sum(n * number of occurrences in left list * number of occurrences in right list). We need something that can take a list and turn it into a number->count mapping. Raku has this out of the box!

Raku calls these Bags. (Again, Raku names things strangely.) Bags do exactly what we want: they are a weighted set.

This is all we need to add for part 2:

my ($lb, $rb) = (@v.Bag, @u.Bag);

say [+] $lb.keys.map: -> $a { $a * $lb{$a} * $rb{$a} };

.Bag turns the array into a bag; we store the left list’s bag in $lb and the right list’s in $rb.

I chose to make the block we are mapping more explicit; it explicitly takes an argument $a. $lb{$a} gives us the weight of that value in the bag, so if $a shows up three times in the left list, $lb{$a} == 3.


I might stick with Raku for the next few days; it’s a fun language that I don’t have many opportunities to use. Raku’s (originally Perl’s) philosophy of “there’s more than one way to do it” (TMTOWTDI) makes it really nice for one-off scripts - and that’s what these AoC solutions are!


  1. Raku has now diverged significantly from Perl. One example is that Raku does not use Perl-Compatible Regular Expressions!
  2. You can also define your own; I may do that in a future solution.
  3. because it takes another operator as an argument, in this case -
  4. The for loop above takes a block!
Tags: