Perl Weekly Challenge 275.

My solutions (task 1 and task 2 ) to the The Weekly Challenge - 275.

Task 1: Broken Keys

Submitted by: Mohammad Sajid Anwar
You are given a sentence, $sentence and list of broken keys @keys.

Write a script to find out how many words can be typed fully.

Example 1
Input: $sentence = "Perl Weekly Challenge", @keys = ('l', 'a')
Output: 0
Example 2
Input: $sentence = "Perl and Raku", @keys = ('a')
Output: 1

Only Perl since the other word two words contain 'a' and can't be typed fully.
Example 3
Input: $sentence = "Well done Team PWC", @keys = ('l', 'o')
Output: 2
Example 4
Input: $sentence = "The joys of polyglottism", @keys = ('T')
Output: 2

I use grep to filter the words of the sentence according to a class of characters made of the keys, and then count the number of elements. The results fit a oneliner.

Example 1:

perl -E '
($s, @k)=@ARGV;@w=split " ",$s; say "sentence: \"$s\" keys: @k -> ", 0+grep{!/[@k]/i}@w;
' "Perl Weekly Chanllenge" l a

Results:

sentence: "Perl Weekly Chanllenge" keys: l a -> 0

Example 2:

perl -E '
($s, @k)=@ARGV;@w=split " ",$s; say "sentence: \"$s\" keys: @k -> ", 0+grep{!/[@k]/i}@w;
' "Perl and Raku" a

Results:

sentence: "Perl and Raku" keys: a -> 1

Example 3:

perl -E '
($s, @k)=@ARGV;@w=split " ",$s; say "sentence: \"$s\" keys: @k -> ", 0+grep{!/[@k]/i}@w;
' "Well done Team PWC" l o

Results:

sentence: "Well done Team PWC" keys: l o -> 2

Example 4:

perl -E '
($s, @k)=@ARGV;@w=split " ",$s; say "sentence: \"$s\" keys: @k -> ", 0+grep{!/[@k]/i}@w;
' "The joys of polyglottism" T

Results:

sentence: "The joys of polyglottism" keys: T -> 2

The corresponding full code is:

 1  # Perl weekly challenge 275
 2  # Task 1:  Broken Keys
 3  #
 4  # See https://wlmb.github.io/2024/06/24/PWC275/#task-1-broken-keys
 5  use v5.36;
 6  die <<~"FIN" unless @ARGV;
 7      Usage: $0 "S" K1 K2...
 8      to count how many words in the sentence S don't contain the broken keys K1 K2...
 9      FIN
10  my ($sentence, @keys)=@ARGV;
11  my @words=split " ",$sentence;
12  say "sentence: \"$sentence\" keys: @keys -> ", 0+grep{!/[@keys]/i}@words;

Examples:

./ch-1.pl "Perl Weekly Chanllenge" l a
./ch-1.pl "Perl and Raku" a
./ch-1.pl "Well done Team PWC" l o
./ch-1.pl "The joys of polyglottism" T

Results:

sentence: "Perl Weekly Chanllenge" keys: l a -> 0
sentence: "Perl and Raku" keys: a -> 1
sentence: "Well done Team PWC" keys: l o -> 2
sentence: "The joys of polyglottism" keys: T -> 2

#+endsrc

Task 2: Replace Digits

Submitted by: Mohammad Sajid Anwar
You are given an alphanumeric string, $str, where each character is either a
letter or a digit.

Write a script to replace each digit in the given string with the value of the
previous letter plus (digit) places.

Example 1
Input: $str = 'a1c1e1'
Ouput: 'abcdef'

shift('a', 1) => 'b'
shift('c', 1) => 'd'
shift('e', 1) => 'f'

Example 2
Input: $str = 'a1b2c3d4'
Output: 'abbdcfdh'

shift('a', 1) => 'b'
shift('b', 2) => 'd'
shift('c', 3) => 'f'
shift('d', 4) => 'h'
Example 3
Input: $str = 'b2b'
Output: 'bdb'
Example 4
Input: $str = 'a16z'
Output: 'abgz'

I use ord and chr to map characters to numbers and back, which yields a oneliner. I deal with possible overflows in the full code below.

Examples:

perl -E '
for(@ARGV){say "$_ -> ", join "", map{/[0-9]/?chr(ord($c)+$_):($c=$_)}split "", $_;}
' a1c1e1 a1b2c3d4 b2b a16z

Results:

a1c1e1 -> abcdef
a1b2c3d4 -> abbdcfdh
b2b -> bdb
a16z -> abgz

The full code is similar, but has a few checks.

 1  # Perl weekly challenge 275
 2  # Task 2:  Replace Digits
 3  #
 4  # See https://wlmb.github.io/2024/06/24/PWC275/#task-2-replace-digits
 5  use v5.36;
 6  die <<~"FIN" unless @ARGV;
 7      Usage: $0 S1 S2...
 8      to replace digits by the previous character shifted the corresponding
 9      number of spaces
10      FIN
11  for(@ARGV){
12      my ($last, $current);
13      my $result=join "", map {($last, $current)=nextchar($last, $_); $current} split "", $_;
14      say "$_ -> $result";
15  }
16  sub nextchar($last, $current){
17      die "Digit before first char" if /[0-9]/ && not defined $last;
18      $current=chr(ord($last)+$current) if /[0-9]/;
19      $last=$current unless /[0-9]/;
20      die "Out of range" unless $current=~/[a-z]/i;
21      return ($last, $current);
22  }

Examples:

./ch-2.pl a1c1e1 a1b2c3d4 b2b a16z

Results:

a1c1e1 -> abcdef
a1b2c3d4 -> abbdcfdh
b2b -> bdb
a16z -> abgz

Other examples:

./ch-2.pl 1 2>&1
./ch-2.pl z1 2>&1

Results:

Digit before first char at ./ch-2.pl line 18.
Out of range at ./ch-2.pl line 21.
Written on June 24, 2024