Perl Weekly Challenge 363.
My solutions (task 1 and task 2 ) to the The Weekly Challenge - 363.
Task 1: String Lie Detector
Submitted by: Mohammad Sajid Anwar
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.
Example 1
Input: $str = "aa — two vowels and zero consonants"
Output: true
Example 2
Input: $str = "iv — one vowel and one consonant"
Output: true
Example 3
Input: $str = "hello - three vowels and two consonants"
Output: false
Example 4
Input: $str = "aeiou — five vowels and zero consonants"
Output: true
Example 5
Input: $str = "aei — three vowels and zero consonants"
Output: true
As all the examples fall into the same pattern, I make a simplified program that solves that pattern only. The input is not really self-referential, as the second part (after the dash) describes the first part. I assume that there are less than ten vowels/consonants and that the structure of the phrase is always the same. This yields a three liner.
Examples:
perl -E '
@N=qw(zero one two three four five six seven eight nine); @N{@N}=(0..9);for(@ARGV){
($s,$v,$c)=($1,$3,$4) if /^\s*(.*?)\s*(-|—)\s*(.*)\s+vowels?\s+and\s+(.*)\s+consonants?$/;
@v=$s=~/([aeiou])/g;say "$_ -> ", @v==$N{$v}&&length($s)-@v==$N{$c}?"T":"F";}
' "aa — two vowels and zero consonants" "iv — one vowel and one consonant" \
"hello - three vowels and two consonants" "aeiou — five vowels and zero consonants" \
"aei — three vowels and zero consonants"
Results:
aa — two vowels and zero consonants -> T
iv — one vowel and one consonant -> T
hello - three vowels and two consonants -> F
aeiou — five vowels and zero consonants -> T
aei — three vowels and zero consonants -> T
I add some flexibility and some checks to the full code.
1 # Perl weekly challenge 363
2 # Task 1: String Lie Detector
3 #
4 # See https://wlmb.github.io/2026/03/02/PWC363/#task-1-string-lie-detector
5 use v5.36;
6 use feature qw(try);
7 my @digit_names = qw(zero one two three four five six seven eight nine);
8 my _to_digit;
9 @name_to_digit{@digit_names} = (0..9);
10 die <<~"FIN" unless @ARGV;
11 Usage: $0 S0 S1...
12 to check if the string Sn correctly describes itself.
13 Each string is of the form s - v vowels and c consonants,
14 where s is a substring, - is a dash or em-dash, and v and c
15 are number names in the range 0-9.
16 FIN
17 for(@ARGV){
18 try {
19 die "Wrong format: $_"
20 unless /^\s*(.*?)\s*(-|—)\s*(.*)\s+vowels?\s+and\s+(.*)\s+consonants?$/;
21 my ($string, $vocals, $consonants) = ($1, map {$name_to_digit{$_}} ($3,$4));
22 my @vocals = $string=~/([aeiou])/g;
23 my @consonants = $string=~/([^aeiou])/g;
24 my $result = @vocals == $vocals && @consonants == $consonants;
25 say "$_ -> ", $result?"True":"False";
26 }
27 catch($e){ warn $e }
28 }
Example:
./ch-1.pl "aa — two vowels and zero consonants" "iv — one vowel and one consonant" \
"hello - three vowels and two consonants" "aeiou — five vowels and zero consonants" \
"aei — three vowels and zero consonants"
Results:
aa — two vowels and zero consonants -> True
iv — one vowel and one consonant -> True
hello - three vowels and two consonants -> False
aeiou — five vowels and zero consonants -> True
aei — three vowels and zero consonants -> True
More flexibility could be attained using, for example, Marpa::R2, to
build a grammar that can parse the strings, their description of the
string and the naming of numbers.
Task 2: Subnet Sheriff
Submitted by: Peter Campbell Smith
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.
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
The numbers separated by dots should by of 8 bits each, and the number after the slash is the number of leftmost bits that should match when an address belongs to a domain. Example 2 is strange, as the last component, 256, doesn’t fit into one byte, though the first three bytes do agree. I don’t know how to interpret that example. For simplicity, I’ll assume four digits base 256 to build the address, even when one digit is larger than 256. I convert both addresses to 32 bit numbers, build a mask of bits that should agree, and check that any bit that desagrees corresponds to a zero in the mask. The result takes a 2+ liner.
Examples
perl -E '
for my($a,$d)(@ARGV){@A=split /\./, $a;($c=$d)=~s[/(\d+)$][];$m=0; $m=2*$m+$_ for((1)x$1,(0)x(32-$1));
@D=split /\./, $c;($A,$D)=map {$t=0; $t=$t*256+$_ for @$_;$t}[@A],[@D];say "$a, $d -> ", +($A^$D)&$m?"F":"T";}
' 192.168.1.45 192.168.1.0/24 10.0.0.256 10.0.0.0/24 172.16.8.9 172.16.8.9/32 \
172.16.4.5 172.16.0.0/14 192.0.2.0 192.0.2.0/25
Results:
192.168.1.45, 192.168.1.0/24 -> T
10.0.0.256, 10.0.0.0/24 -> F
172.16.8.9, 172.16.8.9/32 -> T
172.16.4.5, 172.16.0.0/14 -> T
192.0.2.0, 192.0.2.0/25 -> T
I guess I should have used pack and unpack to build the addresses
and masks, but I realized they still confuse me.
The full code is:
1 # Perl weekly challenge 363
2 # Task 2: Subnet Sheriff
3 #
4 # See https://wlmb.github.io/2026/03/02/PWC363/#task-2-subnet-sheriff
5 use v5.36;
6 use feature qw(try);
7 die <<~"FIN" unless @ARGV and @ARGV%2==0;
8 Usage: $0 I0 D0 I1 D1...
9 to find if the internet address In corresponds to domain Dn.
10 In should be of the form a.b.c.d, with a...d digital numbers, while
11 Dn should consist be of the form a.d.c.d/e, with a...e digital numbers.
12 FIN
13 for my($address, $domain)(@ARGV){
14 try {
15 my @address_parts = split /\./, $address;
16 (my $network = $domain)=~s[/(\d+)$][]; # remove mask bit count
17 die "Missing mask size: $domain" unless defined $1;
18 my $mask_size=$1;
19 my $mask=0;
20 $mask = 2*$mask + $_ for((1)x$1,(0)x(32-$1)); # binary mask
21 my @domain_parts = split /\./, $network;
22 my ($bin_address,$bin_domain) =
23 map {my $t=0; $t=$t*256+$_ for @$_;$t}[@address_parts],[@domain_parts];
24 my $result = ($bin_address^$bin_domain)&$mask;
25 say "$address, $domain -> ", $result?"False":"True";
26 }
27 catch($e){ warn $e }
28 }
Example:
./ch-2.pl 192.168.1.45 192.168.1.0/24 10.0.0.256 10.0.0.0/24 172.16.8.9 172.16.8.9/32 \
172.16.4.5 172.16.0.0/14 192.0.2.0 192.0.2.0/25
Results:
192.168.1.45, 192.168.1.0/24 -> True
10.0.0.256, 10.0.0.0/24 -> False
172.16.8.9, 172.16.8.9/32 -> True
172.16.4.5, 172.16.0.0/14 -> True
192.0.2.0, 192.0.2.0/25 -> True
/;