Perl Weekly Challenge 184.
My solutions (task 1 and task 2 ) to the The Weekly Challenge - 184.
Task 1: Sequence Number
Submitted by: Mohammad S Anwar You are given list of strings in the format aa9999 i.e. first 2 characters can be anything ‘a-z’ followed by 4 digits ‘0-9’.
Write a script to replace the first two characters with sequence starting with ‘00’, ‘01’, ‘02’ etc.
Example 1 Input: @list = ( ‘ab1234’, ‘cd5678’, ‘ef1342’) Output: (‘001234’, ‘015678’, ‘021342’) Example 2 Input: @list = ( ‘pq1122’, ‘rs3334’) Output: (‘001122’, ‘013334’)
#+endexample
If I assume that the first two characters are to be replaced without
checking at all what they are, there is a very simple solution: I get
the arguments from @ARGV
, substitute
the first two characters by a
string using Perl’s magic increment of strings and the capability of
executing code in the replacement part of a substitution. The result
is a compact oneliner.
perl -E '$i="00"; say join ", ", map {s/../$i++/e; $_} @ARGV' ab1234 cd5678 ef1342
perl -E '$i="00"; say join ", ", map {s/../$i++/e; $_} @ARGV' pq1122 rs3334
Results:
001234, 015678, 021342
001122, 013334
The full code is essentially the same, save for checking that the conditions of the problem are satisfied
1 # Perl weekly challenge 184
2 # Task 1: Sequence Number
3 #
4 # See https://wlmb.github.io/2022/09/26/PWC184/#task-1-sequence-number
5 use v5.36;
6 die <<"EOF" unless @ARGV;
7 Usage: $0 s1 [s2 ...]
8 to replace the first 2 characters of strings S1 S2...
9 by a two digit increasing code.
10 The strings should be of the form lldddd where l is a letter a..z
11 and d a digit 0..9.
12 EOF
13 die "More than 100 strings" if @ARGV>100;
14 my $counter="00";
15 say join ", ",
16 map {m/^[a-z]{2}\d{4}$/||die "Bad format $_\n"; s/../$counter++/e; $_}
17 @ARGV;
Example:
./ch-1.pl ab1234 cd5678 ef1342
./ch-1.pl pq1122 rs3334
Results:
001234, 015678, 021342
001122, 013334
Examples with mistakes:
./ch-1.pl Ab1234 cd5678 ef1342
./ch-1.pl 001234 cd5678 ef1342
./ch-1.pl ab123 cd5678 ef1342
Results:
Bad format Ab1234
Bad format 001234
Bad format ab123
The first program was completely tolerant. The full version version is dumb and extremely intolerant.
Task 2: Split Array
Submitted by: Mohammad S Anwar
You are given list of strings containing 0-9 and a-z separated by space only.
Write a script to split the data into two arrays, one for integers and one for alphabets only.
Example 1
Input: @list = ( 'a 1 2 b 0', '3 c 4 d')
Output: [[1,2,0], [3,4]] and [['a','b'], ['c','d']]
Example 2
Input: @list = ( '1 2', 'p q r', 's 3', '4 5 t')
Output: [[1,2], [3], [4,5]] and [['p','q','r'], ['s'], ['t']]
I obtain the input from @ARGV
and assume it is in the correct
format. Then I can split each argument on space and store each
character on an array of numbers or letters. I use the new for
my(...)
construction to iterate over such arrays building an
array of arrays of numbers and an array of arrays of letters. Finally,
I stringify and print the resulting arrays using a recursive
function. The code can be condensed into a somewhat incomprehensible 3-liner.
Example 1:
perl -Mexperimental=for_list -MList::Util=reduce -E '
for my ($N,$L)(map {@n=@l=(); /^[0-9]$/ and push @n, $_ or push @l, $_ for split " ";
[@n], [@l]} @ARGV){ push @N, $N if @$N; push @L, $L if @$L;} say p(@N), " and ", p(@L);
sub p(@X){ "[". join(", ", map{ref $_?p(@$_):$_} @X) ."]"}
' 'a 1 2 b 0' '3 c 4 d'
Results:
[[1, 2, 0], [3, 4]] and [[a, b], [c, d]]
Example 2:
perl -Mexperimental=for_list -MList::Util=reduce -E '
for my ($N,$L)(map {@n=@l=(); /^[0-9]$/ and push @n, $_ or push @l, $_ for split " ";
[@n], [@l]} @ARGV){ push @N, $N if @$N; push @L, $L if @$L;} say p(@N), " and ", p(@L);
sub p(@X){ "[". join(", ", map{ref $_?p(@$_):$_} @X) ."]"}
' '1 2' 'p q r' 's 3' '4 5 t'
Results:
[[1, 2], [3], [4, 5]] and [[p, q, r], [s], [t]]
The full code is
1 # Perl weekly challenge 184
2 # Task 2: Split Array
3 #
4 # See https://wlmb.github.io/2022/09/26/PWC184/#task-2-split-array
5 use v5.36;
6 use experimental qw(for_list);
7 use List::Util qw(reduce);
8
9 sub arr2str(@X){ # recursively converts array to string
10 "[" . join(", ", map{ref $_ eq "ARRAY"? arr2str(@$_):$_} @X) . "]"
11 }
12
13 die <<EOF unless @ARGV;
14 Usage: $0 S1 [S2...]
15 to split strings S1, S2... into arrays of numbers and letters.
16 Each string should contain space separated single letters a-z and/or digits 0-9.
17 EOF
18 for(@ARGV){ # check arguments
19 die "Wrong format: $_" unless /^[a-z0-9](\s+[a-z0-9])*$/;
20 }
21
22 my (@numbers_arr, @letters_arr); # arrays of array refs
23 for my ($numbers,$letters)( # array refs
24 map { # separate each arg into number and letters array refs.
25 my(@numbers, @letters);
26 /^[0-9]$/ and push @numbers, $_ or push @letters, $_ for split " ";
27 [@numbers], [@letters]
28 } @ARGV
29 ) {
30 push @numbers_arr, $numbers if @$numbers; # ignore empty arrays
31 push @letters_arr, $letters if @$letters;
32 }
33 say arr2str(@numbers_arr), " and ", arr2str(@letters_arr);
Examples:
./ch-2.pl 'a 1 2 b 0' '3 c 4 d'
./ch-2.pl '1 2' 'p q r' 's 3' '4 5 t'
Results:
[[1, 2, 0], [3, 4]] and [[a, b], [c, d]]
[[1, 2], [3], [4, 5]] and [[p, q, r], [s], [t]]