TWC 193: Evens and Oddballs

In which we revisit seventh grade, and sing in the key of "A".

TWC Task #1 - Binary String

Task

Calculate all possible binary numbers of size $n.

Observations

In 7th grade, I noticed that you can construct all the binary numbers by starting with 0 and 1, then prepending 0-and-1 to all the prior numbers, doubling the size of the calculated each time:

0
1

00
01
10
11

000
001
010
011
100
101
110
111

I used this method in the Perl solution.

Loosely related: This week, my 5-year-old granddaughter
showed us how she can count to 2_000_000 :
Cora Kate: "one million, two million !!!"

Raku

I could have written:

sub task1 ( UInt $n ) { ^(2**$n) .fmt("\%0{$n}b") }

, but that gives the wrong answer when $n is 0. So instead:

multi sub task1 (       1 ) {       <0 1>        }
multi sub task1 ( UInt $n ) { [X~] (<0 1> xx $n) }

The multi is needed because the X cross-operator will operate on a solo <0 1> by unpacking it, translating to '0' ~ '1', which is not what we want.

Perl

I could have written:

sub task1 ( $n ) { map { sprintf "%0${n}b", $_ } 0 .. (2**$n)-1 }

, but that gives the wrong answer when $n is 0. (Sounds familiar.) So instead:

sub task1 ($n) {
    my @r = ("");

    @r = (  map("0$_", @r),
            map("1$_", @r)  ) for 1..$n;

    return @r;
}

TWC Task #2 - Odd String

Task

All string elements of a given list will have the same "distance between adjacent characters", except one. Return that one string element.

Observations

This task is naturally broken into:

  • Calculating the distance
  • Grouping based on that distance
  • Selecting the group with only one element

The solution will probably be clearer if we keep those aspects separate.

Changing "be" into 1,4 to get the difference of 3 can be replaced with ord, since the base values don't matter, only their relative distances.

Raku

I could have golfed this to:

@L.classify(~*.comb.rotor(2=>-1).map({[-] $_».ord})).values.min(+*)

, but T'is the season of thankfulness and giving. Also, I am not happy with the assumptions that the task is making, so I structured it to better allow for warnings.

sub oddballs ( @list, &matcher ) {
    my %h = @list
            .classify(&matcher)
            .values
            .classify({ .elems == 1 ?? 'Oddball' !! 'Clique' });

    warn "Multiple cliques exist" if %h<Clique>.elems > 1;

    return %h<Oddball>.list;
}
sub neighbor_distances ( Str $s --> Str ) {
    return $s.comb
             .map(&ord)
             .rotor(2 => -1)
             .map({ .[1] - .[0] })
             .Str;
}
sub task2 (@list) {
    die "Must have at least 3 to have an oddball" if @list.elems < 3;

    my @o = oddballs( @list, &neighbor_distances );

    warn "No oddballs found"     if not @o;
    warn "More than one oddball" if @o.elems > 1;

    return @o.head;
}

I want to point out that the "neighbor-difference after numeric translation" is a fine way to specify the desired grouping, but an alternative could also serve:

$s .= trans( ['b'..'z'] => ['a'..'y'] ) until $s.contains: 'a';

Reducing each letter along the alphabet until any of them is an a would produce the same results.

Perl

Using many modules, to better parallel the Raku solution:

use v5.36;
use List::Util       qw<mesh pairvalues>;
use List::MoreUtils  qw<slide>;
use List::Categorize qw<categorize>;

sub diffs ( $s ) {
    state %A_N = mesh ['a'..'z'], [0..25];

    return join ':',
           slide { $b - $a }
           @A_N{ split '', $s };
}
sub oddballs ( @s ) {
    return grep { @{$_} == 1 } 
           pairvalues
           categorize { diffs($_) } @s;
}
sub task2 ( @s ) {
    my @r = oddballs(@s);
    warn if @r != 1;
    return $r[0][0];
}
  • mesh is Raku's Z.
  • slide is Raku's .rotor(2 => -1).
  • categorize is Raku's .classify.
  • pairvalues makes up for Raku's Pairs being true objects.

I used the @-sigil on a hash to access Perl's "hash slicing", allowing all of the letters to be translated in one go.

Leave a comment

About Bruce Gray

user-pic "Util" on IRC and PerlMonks. Frequent speaker on Perl and Raku, but infrequent blogger.