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
Written on October 10, 2022