I’ve been doing the Weekly
Challenges. The
latest
involved list comparison and colour mangling. (Note that this ends
today.)
Just my "core" languages of Perl, Raku, Rust and PostScript this
time, as it's been a busy (and hot) week.
Task 1: Similar List
You are given three list of strings.
Write a script to find out if the first two list are similar with
the help the third list. The third list contains the similar words
map.
As defined, that similar words map was just a series of pairs. My
algorithm will work with that, but will also let you have larger
groups.
PostScript:
/similarlist {
0 dict begin
/similar exch def
/b exch def
/a exch def
Initialise the similarity map.
/simmap 0 dict def
For each list of similar words:
similar {
/simset exch def
Define a "base", which is arbitrarily the first word.
/base simset 0 get def
Let all the other words map to that base word. (I could have done this
more efficiently in PostScript specifically with the <<...>> literal
dict syntax, but I didn't think of it.)
1 1 simset length 1 sub {
simmap exch
simset exch get
base
put
} for
} forall
Then check each pair of words. Return true if all tests pass.
true
0 1 a length 1 sub {
/i exch def
For the words at this position in lists a and b, get either the
similarity map entry for that word, or the word itself. (I didn't
think of this use of dget, a dict fetch with a default value if the
key is not found, when I wrote it, but it's jolly handy; better in
this regard than a dict with a single default value would be.)
simmap a i get dup dget
simmap b i get dup dget
If they don't match, abort and return false.
deepeq not {
pop false
exit
} if
} for
end
} bind def
Rust makes this easier to read with a couple of higher-level
operations that let me specify more "what I want to do" than "how to
do it":
"Iterate over this list, except for the first one."
for v in simset.into_iter().skip(1) {
"Return the value in the map, or this other value if there isn't a
value in the map."
let aw = simmap.get(a[i]).unwrap_or(&a[i]);
Task 2: Nearest RGB
You are given a 6-digit hex color.
Write a script to round the RGB channels to the nearest web-safe
value and return the nearest RGB color.
00 (0), 33 (51), 66 (102), 99 (153), CC (204) and FF (255)
Oh, wow, remember web-safe colours?
The algorithm is very simple: take the nearest multiple of 51 to each
of the three values. All the fiddle is in the parsing and output.
In Raku:
sub nearestrgb($a) {
Treat the colour value as one big number expressed in hexadecimal.
my $s = :16(substr($a, 1));
my @rgb;
Extract each 8-bit component of it in turn, process it, and stick it
on the output list.
for [16, 8, 0] -> $bs {
my $cs = ($s +> $bs) +& 0xff;
my $ct = floor($cs / 51) * 51;
if ($cs - $ct > 25) {
$ct += 51;
}
@rgb.push($ct);
}
Format that output list.
sprintf('#%02X%02X%02X', @rgb[0], @rgb[1], @rgb[2]);
}
Full code on
codeberg.