Perl Weekly Challenge 186.
My solutions (task 1 and task 2 ) to the The Weekly Challenge - 186.
Task 1: Zip List
Submitted by: Mohammad S Anwar
You are given two list @a and @b of same size.
Create a subroutine sub zip(@a, @b) that merge the two list as shown in the example below.
Example
Input: @a = qw/1 2 3/; @b = qw/a b c/;
Output: zip(@a, @b) should return qw/1 a 2 b 3 c/;
zip(@b, @a) should return qw/a 1 b 2 c 3/;
The easy way out is to use a canned solution, such as zip
from
List::MoreUtils
. I assume that the lists are provided as space
separated strings in @ARGV;
perl -MList::MoreUtils=zip -E '@x=split " ", shift;@y=split " ", shift; say join " ", zip @x, @y;
' "1 2 3" "a b c"
Results:
1 a 2 b 3 c
A solution may be built from scratch using perl prototypes to convert input arrays to input array references.
perl -E 'sub zip :prototype(\@\@) ($x,$y){while(@$x or @$y){push @r, shift @$x, shift @$y}
grep {defined} @r} @x=split " ", shift;@y=split " ", shift; say join " ", zip(@x, @y);
' "1 2 3" "a b c"
Results:
1 a 2 b 3 c
The behaviour for lists of uneven length is not well defined. Here I
followed the same convention as in List::MoreUtils
, i.e., if one
list is exhausted I just add the elements of the other list. Actually,
I continue zipping undef
’s and I grep
them out at the end.
The full code is
1 # Perl weekly challenge 186
2 # Task 1: Zip List
3 #
4 # See https://wlmb.github.io/2022/10/10/PWC186/#task-1-zip-list
5 use v5.36;
6 sub zip :prototype(\@\@) ($x,$y){
7 my @result;
8 while(@$x or @$y){ # Until both lists are exhausted
9 push @result, shift @$x, shift @$y
10 }
11 grep {defined} @result # remove undef's
12 }
13 die qq/Usage: $0 "x1 x2..." "y1 y2..."\nto zip two lists/ unless @ARGV==2;
14 my @x=split " ", shift;
15 my @y=split " ", shift;
16 say join " ", zip(@x, @y);
Example:
./ch-1.pl "1 2 3" "a b c"
Task 2: Unicode Makeover
Submitted by: Mohammad S Anwar
You are given a string with possible unicode characters.
Create a subroutine sub makeover($str) that replace the unicode characters
with ascii equivalent. For this task, let us assume it only contains alphabets.
Example 1
Input: $str = 'ÃÊÍÒÙ';
Output: 'AEIOU'
Example 2
Input: $str = 'âÊíÒÙ';
Output: 'aEiOU'
I’m always very confused by Unicode and Perl. I assume that the input
strings are given in @ARGV
. Since they contain unicode strings, I
first decode them. I split them into chars and compare them using
Unicode::Collate to the printable ASCII characters, ignoring all kind
of accents. If they match, they are replaced. Finally, I encode and
print the resulting string. The result fits a three-liner.
perl -MEncode=decode,encode -MUnicode::Collate -E '
sub r($x){state $c=Unicode::Collate->new(level=>3, ignore_level2=>1); $c->cmp($_, $x)==0
&& return $_ for map {chr} (0x20..0x7e); $x;}sub makeover($s){join "", map {r $_}
split "", $s} say encode("UTF-8", makeover(decode("UTF-8", $_))) for(@ARGV);
' ÃÊÍÒÙ âÊíÒÙ
Results:
AEIOU
aEiOU
Maybe it is inefficient to check all ASCII chars instead of checking all possible accents and ornaments, but I don’t know how many of those there are.
The full code is
1 # Perl weekly challenge 186
2 # Task 2: Unicode Makeover
3 #
4 # See https://wlmb.github.io/2022/10/10/PWC186/#task-2-unicode-makeover
5 use v5.36;
6 use Encode qw(decode encode);
7 use Unicode::Collate;
8 sub char2ascii($char){ # Convert single char to ascii
9 state $coll=Unicode::Collate->new(level=>3, ignore_level2=>1); #Ignore accents, check case
10 state @ascii=map {chr} (0x20..0x7e);
11 $coll->cmp($_, $char)==0 && return $_ for @ascii; #return ascii if found
12 $char # default
13 }
14
15 sub makeover($string){ # convert string to ascii
16 join "", map {char2ascii $_} split "", $string
17 }
18
19 die "Usage: $0 S1 [S2...]\nto convert strings S1 S2 etc. to ascii" unless @ARGV;
20 say encode("UTF-8", makeover(decode("UTF-8", $_))) for(@ARGV);
Example:
./ch-2.pl ÃÊÍÒÙ âÊíÒÙ
Results:
AEIOU
aEiOU
Curiously, my code above maps many punctuation characters to space, though they are ASCII punctuation. I guess that at level 3 all punctuation is equivalent.
./ch-2.pl ".,;:{}[]+=?!¿¡﹋"|od -h
Results:
0000000 2020 2020 2020 2020 2020 2020 2020 0a20
0000020