Task #1 is here: Perl Weekly Challenge 363, Task 1
#!/usr/bin/env perl
use v5.38;
use utf8;
use open ':std', ':encoding(UTF-8)';
=head1 COMMENTS
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.
=cut
my %examples = ( Example_1 => "aa — two vowels and zero consonants",
Example_2 => "iv — one vowel and one consonant",
Example_3 => "hello - three vowels and two consonants",
Example_4 => "aeiou — five vowels and zero consonants",
Example_5 => "aei — three vowels and zero consonants"
);
# I decided to provide my own hash to map the numerals to their English
# language equivalents
my %numbers = ( 0 => 'zero',
1 => 'one',
2 => 'two',
3 => 'three',
4 => 'four',
5 => 'five',
6 => 'six',
7 => 'seven',
8 => 'eight',
9 => 'nine',
);
foreach my $example ( sort keys %examples ) {
# Not clear to me if this was deliberate (vs. unintentional copy-paste) but
# most examples actually have em-dashes, but one has just a hyphen, so I had to
# get slightly more sophisticated than I anticipated with my regex for this step
# But this is EXCELLENT practice for real life data munging & data wrangling
my ( $string, $fact, ) = split(/\s+(?:\x{2014}|-)\s+/, $examples{$example});
my $status = q{};
# Yadda-yadda-yadda, is there an O'Reilly book on the Perl Programming Language that
# does NOT have this example of how to turn a string into an array of it's
# substituent characters?
my @letters = split(//, $string);
# I want to count the number of vowels in the first string, and evaluating this statement
# in a scalar context will give me that number
my $vowels = grep { /[aeiou]/ } @letters;
my $consonants = length($string) - $vowels;
# Caveat emptor: some of the examples have a singular vowel, whilst others
# have plural vowels
my ( $v_fact, $c_fact ) = $fact =~ m/(.+) vowels? and (.+) consonant/;
if ( $numbers{$vowels} eq $v_fact and $numbers{$consonants} eq $c_fact ) {
$status = 'true';
}
else {
$status = 'false';
}
print "$example\n";
print "Input: \"", $examples{$example}, "\"\n";
print "Output: ", $status, "\n\n";
}
__END__
OUTPUT from running this Script:
Example_1
Input: "aa — two vowels and zero consonants"
Output: true
Example_2
Input: "iv — one vowel and one consonant"
Output: true
Example_3
Input: "hello - three vowels and two consonants"
Output: false
Example_4
Input: "aeiou — five vowels and zero consonants"
Output: true
Example_5
Input: "aei — three vowels and zero consonants"
Output: true
Task #2 is here: Perl Weekly Challenge 363, Task 2
#!/usr/bin/env perl
use v5.38;
use utf8;
use open ':std', ':encoding(UTF-8)';
use Socket;
=head1 COMMENTS
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:
https://en.wikipedia.org/wiki/IPv4
=cut
my %examples = ( Example_1 => { ip_addr => "192.168.1.45",
domain => "192.168.1.0/24",
},
Example_2 => { ip_addr => "10.0.0.256",
domain => "10.0.0.0/24",
},
Example_3 => { ip_addr => "172.16.8.9",
domain => "172.16.8.9/32",
},
Example_4 => { ip_addr => "172.16.4.5",
domain => "172.16.0.0/14",
},
Example_5 => { ip_addr => "192.0.2.0",
domain => "192.0.2.0/25",
},
);
foreach my $example ( sort keys %examples ) {
my $output = q{};
my $subnet_mask = q{};
my @octets = split(/\./, $examples{$example}{ip_addr});
# Apparently the only allowed valid numbers are between
# 0 and 255, so we'll test for that first.
if ( grep { $_ > 255 } @octets ) {
$output = 'false';
}
else {
# If none of the octets exceed decimal 255 then we need to
# convert the IP addresse and the network into their binary equivalents
my $ip_addr = unpack('B32', inet_aton($examples{$example}{ip_addr}));
my ( $domain, $suffix, ) = split(/\//, $examples{$example}{domain});
$domain = unpack('B32', inet_aton($domain));
# There is likely some slick way to do this that all the cool kids
# use, but I didn't even really grok why IP addresses are built up
# in such a bizarro fashion until this particular Perl Weekly Challenge
# so I derived this algorithm on the fly:
my $subnet_mask = q{};
for my $bit ( 1..32 ) {
if ( $bit > $suffix ) {
$subnet_mask .= 0;
}
else {
$subnet_mask .= 1;
}
}
# I assumed, naively, that having decoded the decimal version of the
# octets into binary 1's and 0's that we were ready to use
# Perl's version of the bitwise AND operator to mask the
# bits in the IP address. Not so. Using bitwise AND on
# the 1's and 0's did not yield output at all like what I
# was expecting.
# It turns out there is one more step; Perl interprets
# my 1's and 0's as strings of characters, NOT binary
# numbers, alas. And thus we need to convert our 1's
# and 0's which are a binary strings into binary integers
# (are we having fun yet?):
my $ip_int = oct("0b" . $ip_addr);
my $mask_int = oct("0b" . $subnet_mask);
my $network_int = oct("0b" . $domain);
# Finally starting with decimals, which we rendered as binary strings
# we end up with binary integers, which we hope is what we want.
# So that we can . . .
# Perform bitwise AND
my $result_int = $ip_int & $mask_int;
# Now we can compare the masked ip_addr to the network domain and see if
# this ip_addr is actually contained in this network (the original task)
if ($result_int == $network_int) {
$output = 'true';
}
else {
$output = 'false'
}
}
print $example, "\n";
print "Input: ip_addr = \"", $examples{$example}{ip_addr}, "\"\n";
print " domain = \"", $examples{$example}{domain}, "\"\n";
print "Output: $output\n\n";
}
__END__
OUTPUT from running this Script:
Example_1
Input: ip_addr = "192.168.1.45"
domain = "192.168.1.0/24"
Output: true
Example_2
Input: ip_addr = "10.0.0.256"
domain = "10.0.0.0/24"
Output: false
Example_3
Input: ip_addr = "172.16.8.9"
domain = "172.16.8.9/32"
Output: true
Example_4
Input: ip_addr = "172.16.4.5"
domain = "172.16.0.0/14"
Output: true
Example_5
Input: ip_addr = "192.0.2.0"
domain = "192.0.2.0/25"
Output: true