Raku Weekly Challenge : Week 327

Week 327 : 2025-06-23

Part 1

Well as often happens with a weekly challenge I thought to myself. "Hmmm, pretty sure this can be solved with set theory". One quick look at the Raku docs with a detour to Wikipedia to double check we have the Set Difference operator.

Image by Patilvarad - Own work, CC BY-SA 4.0, Link.

In this case A is all the items in the list from 1 to n where n is the length of the list B. And we can do this in Raku with the following code.


(1..@b.elems) ∖ @b
      

A few things to note, that's not the \ character but the Unicode symbol for the set operator. If you don't want to use Unicode you can instead use (-). Also you have to be careful, the values coming from the command line are IntStr typed not Int and the Set operator will treat them as different because of this. So you need to case your list to Int before you compare. Bearing this in mind the final code is.


#!/usr/bin/env raku

#|(Given a list of integers find all the
values not in the list from 1..n where
n is the length of the list)
sub MAIN(
    *@a where @a.all ~~ UInt #= A list of unsigned integers
) {
    my @list = @a.map( *.Int );
    ((1..@list.elems) ∖ @list).keys.sort.say;
}
      

As a QuantHash has the values of the result in the keys we need to get that, and I feel it's always nice to return a sorted list when we can hence the final result.

After writing and submitting my answer I realised I could easily do this as a single line without the mapping step first.


((1..@a.elems) ∖ @a.map(*.Int)).keys.sort.say;
      

But there's only so much fiddling I'm going to do with a challenge. Still I figured I'd include it here as a note to the interested.

Part 2

So for Part 2 we need to do more than a one line answer which is nice. (I'm counting part one as a one liner because the first line is just tidying up the input and I could have done it inline but it would have made it a bit nastier to read.) Anyway for Part 2 we are looking for all the pairs of numbers that share the minimum absolute difference.

Now at first glance you may think this means comparing all the pairs of numbers but a bit more thought will point out that if we sort our list before doing anything with it then we can just check adjacent pairs. As the rules state the list has to be distinct values we know that for any pair in the sorted list they will either be the minimum difference apart or something larger. Also by sorting the list we don't have to worry about the absolute part, the first number in the pair is going to be the smaller one.

To get the list broken into consectuive pairs we can use one of my most favourite Raku methods rotor using the paired argument version we can get overlapping pairs. If we do this on the sorted list we get our consecutive pairs.


@a.sort.rotor(2 => -1)
      

We then loop through this list getting the different between the two numbers and comparing it to the current minimum. We can use another nice feature of Raku, by setting the initial value of the minimum difference to Infinity we don't have to have a special step for the first test. If our new difference is less than our current minimum we set the current to it's value and clear our return list. Then if our current different equals the minimum we push the pair into the return list. Finally after the loop is done we return the return list.


#!/usr/bin/env raku

#|(Given a list of integers return
a list of pairs which share the minimum absolute
difference found in the list)
sub MAIN (
    *@a where @a.all ~~ Int && @a.unique.elems == @a.elems #= A list of distinct integers
) {
    my @ret = [];
    my $mad = Inf;
    for @a.sort.rotor(2 => -1) -> $pair {
        my $diff = $pair[1] - $pair[0];
        if ( $diff < $mad ) {
            @ret = [];
            $mad = $diff;
        }
        if ( $diff == $mad ) {
            @ret.push($pair);
        }
    }
    @ret.say;
}
      

Note that we are also checking that we have a list of Integers and that it is distinct values by checking the length of the list against the length of its unique elements. There is an argument to be made for doing this as a couple of mapping passes, one to get the difference for each pair and note the minimum and an second to remove the paris with higher differences but I feel this way works just as well and means you are only going through the list once (not including the sort). So I think I will keep it this way.