I’ve been doing the Weekly
Challenges. The
latest
involved string parsing and subnet masks. (Note that this ends today.)
I didn't end up feeling particularly enthused about either of
these problems, so I only did them in the core languages (Perl and
Raku, Rust because I find it the best language for actually working up
an algorithm, and PostScript because I love it).
Task 1: String Lie Detector
You are given a string.
Write a script that parses a self-referential string and determines
whether its claims about itself are true. The string will make
statements about its own composition, specifically the number of
vowels and consonants it contains.
I have a string parser, of sorts.
sub stringliedetector($a) {
Split input into list of words.
my @words = $a.split(' ').grep({$_ ne ""});
Count vowels and consonants in first word.
my $vowels = 0;
my $consonants = 0;
for @words[0].comb -> $c {
if ($c ~~ /<[aeiou]>/) {
$vowels++;
} else {
$consonants++;
}
}
Build numeric lookup table.
my %w2n = (
"zero" => 0,
"one" => 1,
"two" => 2,
"three" => 3,
"four" => 4,
"five" => 5,
"six" => 6,
"seven" => 7,
"eight" => 8,
"nine" => 9,
"ten" => 10,
);
my $valid = True;
my $mc = 0;
Iterate through descriptive words.
for 2 .. @words.end -> $wi {
my $w = @words[$wi];
If it's a number, set the number field.
if (%w2n{$w}:exists) {
$mc = %w2n{$w};
If it's "vowel" or "vowels", match number field to vowel count.
} elsif ($w ~~ /^vowels?$/) {
if ($mc != $vowels) {
$valid = False;
}
If it's "consonant" or "consonants", match number field to consonant count.
} elsif ($w ~~ /^consonants?$/) {
if ($mc != $consonants) {
$valid = False;
}
}
And of any of those things has failed, drop out of the loop.
if (!$valid) {
last;
}
}
$valid;
}
This relies on the input being correct: "three vowels consonants"
would match on three of each.
Task 2: Subnet Sheriff
You are given an IPv4 address and an IPv4 network (in CIDR format).
Write a script to determine whether both are valid and the address
falls within the network. For more information see the Wikipedia
article.
I only do some of the validation… Use the library modules!
Parse an address into a 32-bit integer. This will incorrectly pass
addresses with a number of fields other than 4. Perl:
sub parseaddr($a) {
my $out = 0;
foreach my $os (split /\./, $a) {
if ($os >= 0 && $os < 256) {
$out *= 256;
$out += $os;
} else {
return -1;
}
}
$out;
}
sub subnetsheriff($addrs, $nets) {
Parse the address.
my $addr = parseaddr($addrs);
if ($addr >= 0) {
Split the network and parse the address part.
my @netsc = split '/', $nets;
my $netmask = parseaddr($netsc[0]);
if ($netmask >= 0) {
Parse the prefix length.
my $netlen = 0 + $netsc[1];
if ($netlen >= 0 && $netlen <= 32) {
Build an address mask, and generate the first and last addresses in
the range using it.
my $bits = (1 << (32 - $netlen)) - 1;
my $netlo = $netmask & !$bits;
my $nethi = $netmask | $bits;
Check that the address is within the range.
if ($addr >= $netlo && $addr <= $nethi) {
return 1;
}
}
}
}
Or if any of this has failed, report failure.
0;
}
Full code on
codeberg.