Perl Weekly Challenge 277: arrays and lists
This post presents my solutions to the Perl Weekly Challenge 277.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 277 - Task 1 - Raku
- PWC 277 - Task 2 - Raku
- PWC 277 - Task 1 in PostgreSQL PL/Perl
- PWC 277 - Task 2 in PostgreSQL PL/Perl
- PWC 277 - Task 1 in PostgreSQL PL/PgSQL
- PWC 277 - Task 2 in PostgreSQL PL/PgSQL
- PWC 277 - Task 1 in PL/Java
- PWC 277 - Task 2 in PL/Java
- PWC 277 - Task 1 in Python
- PWC 277 - Task 2 in Python
Raku Implementations
PWC 277 - Task 1 - Raku Implementation
The first task was about finding common words in two list of words, where each word appaears only once in each list.sub MAIN( :@left, :@right ) {
my %left-bag = Bag.new( @left );
my %right-bag = Bag.new( @right );
my @matches;
my @single-left = %left-bag.kv.grep( -> $k, $v { $v == 1 } ).map( *[ 0 ] );
my @single-right = %right-bag.kv.grep( -> $k, $v { $v == 1 } ).map( *[ 0 ] );
for @single-left -> $left {
@matches.push( $left ) if ( @single-right.grep( * ~~ $left ) );
}
@matches.elems.say;
}
I first create a
Bag
to count how many times a word appears in every list, then I filter out the single words from each list.
Last I do a loop to find out which words do match, storing them into the @matches
array and then providing the count of the elements.
PWC 277 - Task 2 - Raku Implementation
Given a list of integers, find out which pairs are strong: a strong pair has an absolute value of the difference less than the min value of the pair.sub MAIN( *@nums where { @nums.grep( * ~~ Int ).elems == @nums.elems } ) {
# A pair of integers x and y is called strong pair if it satisfies: 0 < |x - y| < min(x, y).
my @strong;
for 0 ..^ @nums.elems - 1 -> $i {
for $i ^..^ @nums.elems -> $j {
@strong.push: [ @nums[ $i ], @nums[ $j ] ] if ( 0 < abs( @nums[ $i ] - @nums[ $j ] ) < min( @nums[ $i ], @nums[ $j ] ) );
}
}
@strong.elems.say;
}
I use a nested loop to check the condition.
PL/Perl Implementations
PWC 277 - Task 1 - PL/Perl Implementation
I keep two lists of words where I store only the single appearing words in every list. Then, with a loop, I compare the two single-word lists one against the other to find out the matches.CREATE OR REPLACE FUNCTION
pwc277.task1_plperl( text[], text[] )
RETURNS int
AS $CODE$
my ( $left, $right ) = @_;
my @single_left;
my @single_right;
for my $word ( $left->@* ) {
push @single_left, $word if ( scalar( grep { $_ eq $word } $left->@* ) == 1 );
}
for my $word ( $right->@* ) {
push @single_right, $word if ( scalar( grep { $_ eq $word } $right->@* ) == 1 );
}
my $counter = 0;
for my $word ( @single_left ) {
$counter++ if ( grep { $_ eq $word } @single_right );
}
return $counter;
$CODE$
LANGUAGE plperl;
PWC 277 - Task 2 - PL/Perl Implementation
Same implementation of the Raku solution, only more verbose because I define two internal functionsmin
and abs
to compute the conditions.
CREATE OR REPLACE FUNCTION
pwc277.task2_plperl( int[] )
RETURNS int
AS $CODE$
my ( $nums ) = @_;
my @strong;
my $abs = sub {
return $_[ 0 ] * - 1 if ( $_[ 0 ] < 0 );
return $_[ 0 ];
};
my $min = sub {
return $_[ 0 ] if ( $_[ 0 ] < $_[ 1 ] );
return $_[ 1 ];
};
for my $i ( 0 .. $nums->@* - 2 ) {
for my $j ( $i + 1 .. $nums->@* - 1 ) {
my ( $left, $right ) = ( $nums->@[ $i ], $nums->@[ $j ] );
push @strong, [ $left, $right ] if ( $abs->( $left - $rigth ) > 0 && $abs->( $left - $right ) < $min->( $left, $right ) );
}
}
return scalar( @strong );
$CODE$
LANGUAGE plperl;
PostgreSQL Implementations
PWC 277 - Task 1 - PL/PgSQL Implementation
A single query can solve the problem.CREATE OR REPLACE FUNCTION
pwc277.task1_plpgsql( words1 text[], words2 text[] )
RETURNS int
AS $CODE$
WITH w1 AS (
SELECT w::text
FROM unnest( words1 ) w
GROUP BY w
HAVING count(*) = 1
),
w2 AS (
SELECT w::text
FROM unnest( words2 ) w
GROUP BY w
HAVING count(*) = 1
)
SELECT count( l.w )
FROM w1 l, w2 r
WHERE l.w = r.w
$CODE$
LANGUAGE sql;
The materialzed queries
w1
and w2
provide a list of words that appear only once in their list.
The last SELECT
joins the lists to find out which words appear in both the lists, and finally a count
provides the result.
PWC 277 - Task 2 - PL/PgSQL Implementation
I use a nested loop, as in PL/Perl, and a query to compute themin
value.
Note that, in order to have abs
to be greater than zero, it does suffice to test only if the numbers are not the same.
CREATE OR REPLACE FUNCTION
pwc277.task2_plpgsql( nums int[] )
RETURNS int
AS $CODE$
DECLARE
strong_counter int := 0;
current int := 0;
BEGIN
FOR i IN 1 .. array_length( nums, 1 ) - 1 LOOP
FOR j in ( i + 1 ) .. array_length( nums, 1 ) LOOP
SELECT min( x )
INTO current
FROM unnest( array[ nums[ i ], nums[ j ] ] ) x;
IF current > abs( nums[ i ] - nums[ j ] ) AND nums[ i ] <> nums[ j ] THEN
strong_counter := strong_counter + 1;
END IF;
END LOOP;
END LOOP;
RETURN strong_counter;
END
$CODE$
LANGUAGE plpgsql;
Java Implementations
PWC 277 - Task 1 - PostgreSQL PL/Java Implementation
I use the stream API here. With a mapcounting
I store every word with two integers, one that will count the number of appeareance in the first list and the second in the second list respectively.
public static int task1_pljava( String[] words1, String[] words2 ) throws SQLException {
logger.log( Level.INFO, "Entering pwc277.task1_pljava" );
final Map<String, Integer[]> counting = new HashMap<String, Integer[]>();
Stream.of( words1 ).forEach( current -> {
Integer[] count = { 0, 0 };
counting.putIfAbsent( current, count );
count = counting.get( current );
count[ 0 ]++;
counting.put( current, count );
} );
Stream.of( words2 ).forEach( current -> {
Integer[] count = { 0, 0 };
counting.putIfAbsent( current, count );
count = counting.get( current );
count[ 1 ]++;
counting.put( current, count );
} );
return (int) counting.entrySet().stream().filter( current -> {
Integer[] count = current.getValue();
return count[ 0 ] == count[ 1 ] && count[ 0 ] == 1;
} ).count();
}
The counting of appearances is done first computing a
Stream.of
from the array, then forEach
puts into the counting
map the array with zero values if not already there. if the entry is then in the map, I increment the cell count.
Last, I extract the entrySet
stream filtering those that have both cells equal to 1
, count
-ing the results and returning the value.
PWC 277 - Task 2 - PostgreSQL PL/Java Implementation
Again, using the stream API, I iterate over the indexes of thenumbers
array.
public static final int task2_pljava( int[] numbers ) throws SQLException {
logger.log( Level.INFO, "Entering pwc277.task2_pljava" );
final int[] c = new int[]{ 0 };
IntStream.range( 0, numbers.length - 1 )
.forEach( i -> {
c[ 0 ] += IntStream.range( i + 1, numbers.length )
.filter( j -> {
return numbers[ i ] != numbers[ j ]
&& Math.abs( numbers[ i ] - numbers[ j ] ) < Math.min( numbers[ i ], numbers[ j ] );
} ).count();
} );
return c[ 0 ];
}
First, I create a stream over the indexes assigning to the value
i
, then forEach
index I get another stream range (assigned to j
), filter
-ing so that the bs and min
values satisfy the condition.
Python Implementations
PWC 277 - Task 1 - Python Implementation
Similar solution to PL/Perl implementation, but there is the need to separate the arrays using a pipe symbol on the command line.import sys
# task implementation
# the return value will be printed
def task_1( args ):
words1 = []
words2 = []
first = True
for w in args:
if w == '|':
first = False
if first:
words1.append( w )
else:
words2.append( w )
single1 = []
single2 = []
for w in words1:
if len( list( filter( lambda x: x == w, words1 ) ) ) == 1:
if not w in single1:
single1.append( w )
for w in words2:
if len( list( filter( lambda x: x == w, words2 ) ) ) == 1:
if not w in single2:
single2.append( w )
counting = 0
for w in single1:
if w in single2:
counting += 1
return counting
# invoke the main without the command itself
if __name__ == '__main__':
print( task_1( sys.argv[ 1: ] ) )
I keep two lists of words appearing once on their list, and then count how many matches are there.
PWC 277 - Task 2 - Python Implementation
A nested loop on the indexes. I append a string representing both the numbers into thestrong
array, returning then the size of the list.
import sys
# task implementation
# the return value will be printed
def task_2( args ):
nums = list( map( int, args ) )
strong = []
for i in range( 0, len( nums ) - 1 ):
for j in range( i + 1, len( nums ) ):
if nums[ i ] != nums[ j ] and abs( nums[ i ] - nums[ j ] ) < min( nums[ i ], nums[ j ] ):
strong.append( str( nums[ i ] ) + "<->" + str( nums[ j ] ) )
return len( strong )
# invoke the main without the command itself
if __name__ == '__main__':
print( task_2( sys.argv[ 1: ] ) )