Perl Weekly Challenge 266: words and matrix indexes
This post presents my solutions to the Perl Weekly Challenge 266.I keep doing the Perl Weekly Challenge in order to mantain my coding skills in good shape, as well as in order to learn new things, with particular regard to Raku, a language that I love.
This week, I solved the following tasks:
- PWC 266 - Task 1 - Raku
- PWC 266 - Task 2 - Raku
- PWC 266 - Task 1 in PostgreSQL PL/Perl
- PWC 266 - Task 2 in PostgreSQL PL/Perl
- PWC 266 - Task 1 in PostgreSQL PL/PgSQL
- PWC 266 - Task 2 in PostgreSQL PL/PgSQL
- PWC 266 - Task 1 in PL/Java
- PWC 266 - Task 2 in PL/Java
- PWC 266 - Task 1 in Python
- PWC 266 - Task 2 in Python
Raku Implementations
PWC 266 - Task 1 - Raku Implementation
The first task was about finding words from two given statements so that every word appears only one time in one of the two sentences and does not appear in the other.sub MAIN( Str $left, Str $right ) {
my $bag-left = Bag.new( $left.split( / \W+ / ).map( { .lc } ) );
my $bag-right = Bag.new( $right.split( / \W+ / ).map( { .lc } ) );
my @found-words;
@found-words.push: $_ for $bag-left.keys.grep( -> $left, { $bag-left{ $left } == 1 && ! $bag-right.keys.grep( { $left eq $_ } ) } );
@found-words.push: $_ for $bag-right.keys.grep( -> $right, { $bag-right{ $right } == 1 && ! $bag-left.keys.grep( { $right eq $_ } ) } );
@found-words.join( ', ' ).say;
}
The above is probably a too much expensive implementation. The idea is to use two
Bag
s to classify and count the number of words for every statement, then to push into @found-words
array all the words that are keys for one of the bags assuming it appears with a counting of one and does not matches into the keys of the other bag.
A possible alternative and simplest solution, could have been to concatenate the two sentences, split into words, and extract only those words with a counting of one.
PWC 266 - Task 2 - Raku Implementation
Given a suared matrix, ensure that only the diagonal and antidiagonal have non-zero elements.sub MAIN() {
my @matrix = [1, 0, 0, 2],
[0, 3, 4, 0],
[0, 5, 6, 0],
[7, 0, 0, 1],
;
my @indexes;
my $size = @matrix[ 0 ].elems;
for 0 ..^ $size {
@indexes.push: [ $_, $_ ]; # main diagonal
@indexes.push: [ $_, $size - $_ - 1 ]; # antidiagonal
}
my $row-index = 0;
for @matrix -> $row {
my @zeros = $row.grep( { $_ != 0 }, :k ).map( { [ $row-index, $_ ] } );
say 'False' and exit if @zeros.elems != $size / 2;
say 'False' and exit if @indexes.grep( * eq any( @zeros ) ).elems != @zeros.elems;
$row-index++;
}
'True'.say;
}
The above implementation is probably too complex: I create an
@indexes
array with the positions of the main diagonal and anti-diagonal.
then I iterate over the @matrix
elements and checks where the @zeros
elements are. If the zeros are at indexes that are not fully matched into the @indexes
array it means that the matrix has zeros in the diagonal or anti-diagonal, thus it is not good for the aim.
PL/Perl Implementations
PWC 266 - Task 1 - PL/Perl Implementation
I use two bags, similarly to what happens in Raku. However, this time I return all the words from a bag that do not appear in the other sentence (so not checking the other bag).CREATE OR REPLACE FUNCTION
pwc266.task1_plperl( text, text )
RETURNS SETOF text
AS $CODE$
my ( $left, $right ) = @_;
my $bag_left = {};
my $bag_right = {};
$bag_left->{ lc $_ }++ for ( split( /\W+/, $left ) );
$bag_right->{ lc $_ }++ for ( split( /\W+/, $right ) );
for my $word ( keys $bag_left->%* ) {
next if $bag_left->{ $word } != 1;
next if $right =~ /$word/i;
return_next( $word );
}
for my $word ( keys $bag_right->%* ) {
next if $bag_right->{ $word } != 1;
next if $left =~ /$word/i;
return_next( $word );
}
return undef;
$CODE$
LANGUAGE plperl;
PWC 266 - Task 2 - PL/Perl Implementation
A much simpler approach: I iterate over all the elemens and check if I’m on a diagonal or antidiagonal or not.CREATE OR REPLACE FUNCTION
pwc266.task2_plperl( int[][] )
RETURNS boolean
AS $CODE$
my ( $matrix ) = @_;
my $size = scalar $matrix->@[ 0 ]->@*;
for my $row ( 0 .. $size - 1 ) {
for my $column ( 0 .. $size - 1 ) {
# is this the main diagonal?
# is the antidiagonal?
if ( $row == $column || $column == $size - $row - 1 ) {
return 0 if ( $matrix->@[ $row ]->[ $column ] == 0 );
}
else {
return 0 if ( $matrix->@[ $row ]->[ $column ] != 0 );
}
}
}
return 1;
$CODE$
LANGUAGE plperl;
PostgreSQL Implementations
PWC 266 - Task 1 - PL/PgSQL Implementation
A single query to solve the task.CREATE OR REPLACE FUNCTION
pwc266.task1_plpgsql( l text, r text )
RETURNS SETOF text
AS $CODE$
WITH left_words AS (
SELECT w, count( w ) as c
FROM regexp_split_to_table( l, '\W+' ) w
GROUP BY 1
)
, right_words AS (
SELECT w, count( w ) as c
FROM regexp_split_to_table( R, '\W+' ) w
GROUP BY 1
)
SELECT w
FROM left_words
WHERE w NOT IN ( SELECT w FROM right_words )
AND c = 1
UNION
SELECT w
FROM right_words
WHERE w NOT IN ( SELECT w FROM left_words )
AND c = 1
;
$CODE$
LANGUAGE sql;
The two CTEs provide the words of the sentences with their frequence, then I select all the words from one list that do not appear in the other list union the opposite query.
PWC 266 - Task 2 - PL/PgSQL Implementation
Same approach as in PL/Perl, but since arrays are monodimensional, the function accepts the size of the row of the matrix and computes dynamically the index depending on an offset.CREATE OR REPLACE FUNCTION
pwc266.task2_plpgsql( s int, matrix int[] )
RETURNS boolean
AS $CODE$
DECLARE
w int := 0;
BEGIN
FOR r IN 1 .. s LOOP
FOR c IN 1 .. s LOOP
w = ( r - 1 ) * s + c;
IF r = c OR c = s - r + 1 THEN
IF matrix[ w ] = 0 THEN
RETURN false;
END IF;
ELSE
IF matrix[ w ] <> 0 THEN
RETURN false;
END IF;
END IF;
END LOOP;
END LOOP;
RETURN true;
END
$CODE$
LANGUAGE plpgsql;
Java Implementations
PWC 266 - Task 1 - PostgreSQL PL/Java Implementation
Same approach as in PL/Perl: use two hashes as bags. Thededuplicate
method extracts the list of words that appear only in one bag and not in the other.
public class Task1 {
private final static Logger logger = Logger.getAnonymousLogger();
@Function( schema = "pwc266",
onNullInput = RETURNS_NULL,
effects = IMMUTABLE )
public static final String[] task1_pljava( String left, String right ) throws SQLException {
logger.log( Level.INFO, "Entering pwc266.task1_pljava" );
Map<String, Integer> bag_left = new HashMap<String, Integer>();
Map<String, Integer> bag_right = new HashMap<String, Integer>();
classify( bag_left, left );
classify( bag_right, right );
List<String> words = deduplicate( bag_left, bag_right );
String returning[] = new String[ words.size() ];
int i = 0;
for ( String word : words )
returning[ i++ ] = word;
return returning;
}
private static final List<String> deduplicate( Map<String, Integer> bag_left,
Map<String, Integer> bag_right ) {
List<String> words = new LinkedList<String>();
for ( String word : bag_left.keySet() ) {
if ( bag_left.get( word ) == 1 && ! bag_right.containsKey( word ) )
words.add( word );
}
for ( String word : bag_right.keySet() ) {
if ( bag_right.get( word ) == 1 && ! bag_left.containsKey( word ) )
words.add( word );
}
return words;
}
private static final void classify( Map<String, Integer> bag, String sentence ) {
for ( String word : sentence.split( "\\W+" ) ) {
word = word.toLowerCase();
int count = 1;
if ( bag.containsKey( word ) )
count += bag.get( word );
bag.put( word, count );
}
}
}
PWC 266 - Task 2 - PostgreSQL PL/Java Implementation
Same approach as in PL/PgSQL: iterate over the matrix, that is a monodimensional array, and check if I’m on the main diagonal or the antidiagonal.public class Task2 {
private final static Logger logger = Logger.getAnonymousLogger();
@Function( schema = "pwc266",
onNullInput = RETURNS_NULL,
effects = IMMUTABLE )
public static final boolean task2_pljava( int dim, int[] matrix ) throws SQLException {
logger.log( Level.INFO, "Entering pwc266.task2_pljava" );
for ( int row = 0; row < dim; row++ ) {
for ( int col = 0; col < dim; col++ ) {
if ( row == col || col == dim - row - 1 ) {
if ( matrix[ row * dim + col ] == 0 )
return false;
}
else if ( matrix[ row * dim + col ] != 0 )
return false;
}
}
return true;
}
}
Python Implementations
PWC 266 - Task 1 - Python Implementation
Same approach as in PL/Perl, with a few more iterations. import sys
# task implementation
# the return value will be printed
def task_1( args ):
left = args[ 0 ]
right = args[ 1 ]
bag_left = {}
bag_right = {}
# classify
for w in left.split():
c = 1
w = w.lower()
if w in bag_left:
c += bag_left[ w ]
bag_left[ w ] = c
for w in right.split():
c = 1
w = w.lower()
if w in bag_right:
c += bag_right[ w ]
bag_right[ w ] = c
words = []
for w in bag_left:
if bag_left[ w ] == 1 and not w in bag_right:
words.append( w )
for w in bag_right:
if bag_right[ w ] == 1 and not w in bag_left:
words.append( w )
return words
# invoke the main without the command itself
if __name__ == '__main__':
print( task_1( sys.argv[ 1: ] ) )
PWC 266 - Task 2 - Python Implementation
Same approach as in PL/Java.import sys
# task implementation
# the return value will be printed
def task_2( matrix ):
size = len( matrix[ 0 ] )
for row in range( 0, size ):
for col in range( 0, size ):
if row == col or col == size - row - 1:
if matrix[ row ][ col ] == 0:
return 'False'
elif matrix[ row ][ col ] != 0:
return 'False'
return 'True'
# invoke the main without the command itself
if __name__ == '__main__':
matrix = [
[1, 0, 0, 2],
[0, 3, 4, 0],
[0, 5, 6, 0],
[7, 0, 0, 1],
]
print( task_2( matrix ) )