I’ve been doing the Weekly
Challenges. The
latest
involved word list comparison and matrix validation. (Note that this
ends today.)
Task 1: Uncommon Words
You are given two sentences, $line1
and $line2
.
Write a script to find all uncommmon words in any order in the given
two sentences. Return ('') if none found.
A word is uncommon if it appears exactly once in one of the
sentences and doesn't appear in other sentence.
For me this turned out to hinge on destructuring assignments. In Perl,
for example:
For each input sentence, I'll want two data structures: a list of
words that appear only once, and a set (or hash, in languages that
don't have a set type) of words that appear at all.
sub getlistset($a) {
A list of words that appear at all:
my @la = split ' ', $a;
A hash of word counts:
my %ca;
map {$ca{$_}++} @la;
Use the hash to filter the list to only those words that appear
exactly once.
@la = grep {$ca{$_} == 1} @la;
Then return a tuple of word list and hash.
return (\@la, \%ca);
}
For the main function, I run this on each input sentence.
sub uncommonwords($a, $b) {
my ($la, $sa) = getlistset($a);
my ($lb, $sb) = getlistset($b);
Then initialise the output list.
my @out;
Do a two-way comparison: unique words in sentence A vs all words in
sentence B, then unique words in B vs all words in A.
foreach my $r ([$la, $sb], [$lb, $sa]) {
my ($wl, $t) = @{$r};
For each word,
foreach my $w (@{$wl}) {
if it doesn't appear at all in the other sentence,
unless (exists $t->{$w}) {
add it to the output list.
push @out, $w;
}
}
}
return \@out;
}
In Rust I have some more advanced tools, like the Counter
and
HashSet
classes, but the basic logic is the same:
fn getlistset(a: &str) -> (Vec<&str>, HashSet<&str>) {
let mut la = a.split(' ').collect::<Vec<&str>>();
let ca = la.iter().copied().collect::<Counter<&str>>();
la.retain(|k| *ca.get(k).unwrap() == 1);
(la, ca.keys().copied().collect::<HashSet<&str>>())
}
fn uncommonwords(a: &str, b: &str) -> Vec<String> {
let (la, sa) = getlistset(a);
let (lb, sb) = getlistset(b);
let mut out = Vec::new();
for (&ref wl, &ref t) in [(&la, &sb), (&lb, &sa)] {
for w in wl {
if !t.contains(w) {
out.push(w.to_string());
}
}
}
out
}
Task 2: X Matrix
You are given a square matrix, $matrix
.
Write a script to find if the given matrix is X Matrix.
A square matrix is an X Matrix if all the elements on the main diagonal and antidiagonal are non-zero and everything else are zero.
By contrast this is a much easier problem. The constraint is a little
bit like a diagonal matrix, or two of them superimposed. But it's
simply a matter of looking at each value, working out from its
coordinates whether it should be zero or non-zero, and verifying.
Raku:
sub xmatrix(@a) {
my $order = @a.elems - 1;
my $valid = True;
for (0 .. @a.end) -> $y {
for (0 .. @a[$y].end) -> $x {
If we're on the main diagonal or antidiagonal, fail if the element is zero.
if ($x == $y || $x == $order - $y) {
if (@a[$y][$x] == 0) {
$valid = False;
}
Otherwise fail if it's not zero.
} else {
if (@a[$y][$x] != 0) {
$valid = False;
}
}
unless ($valid) {
last;
}
}
unless ($valid) {
last;
}
}
return $valid;
}
With languages that provide an enumerate
-type function this is
easier to express. (Yes, Raku does this with kv
but it's hard to
find if you don't already know what they've chosen to call it, and I
didn't spot it in time.) JavaScript:
function xmatrix(a) {
const order = a.length - 1;
let valid = true;
a.forEach((row, y) => {
row.forEach((value, x) => {
if (x == y || x == order - y) {
if (value == 0) {
valid = false;
}
} else {
if (value != 0) {
valid = false;
}
}
})
});
return valid;
}
Full code on
github.
Comments on this post are now closed. If you have particular grounds for adding a late comment, comment on a more recent post quoting the URL of this one.