Perl Weekly Challenge 329: mangling strings

This post presents my solutions to the Perl Weekly Challenge 329.
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:
The PL/Perl implementations are very similar to a pure Perl implementation, even if the PostgreSQL environment could involve some more constraints. Similarly, the PL/PgSQL implementations help me keeping my PostgreSQL programming skills in good shape.

Raku Implementations

PWC 329 - Task 1 - Raku Implementation

The first task was about finding out all unique numbers hidden within a give string.

sub MAIN( Str $string where { $string ~~ / <[a..zA..Z0..9]>+ / } ) {
    my %digits;
    for ( $string ~~ m:g/ <[0..9]>+ / ).values {
		%digits{ $_ }++;
    }

    %digits.keys.sort.join( ',' ).say;
}



I use an hash %digits to keep unique values, and a regular expression to match every digit only substring. At last, I print out all the numbers sorted.

PWC 329 - Task 2 - Raku Implementation

The second task was about to find out the longest substring that begins and ends with the same letter but with different case.

sub MAIN( Str $string where { $string ~~ / <[a..zA..Z]>+ / } ) {
    exit( 0 ) if $string.chars <= 1;

    my @chars = $string.comb;
    my $found;

    for 0 ..^ @chars.elems {
		my $current = @chars[ $_ ];
		my $needle  = $current.uc;
		$needle = $current.lc if ( $current ~~ / <[A..Z]> / );

		my $last-index = @chars[ $_ .. * - 1 ].grep( * ~~ $needle, :k ).max;
		my $match = @chars[ $_ .. $last-index ].join;
		$found = $match if ( ! $found || $match.chars > $found.chars );
    }

    $found.say;

}



I iterate over all the @chars of the given string, extracting the $current and the searched opposite case one $needle. Then I use grep to extract the index of the last $needle, and then rebuild the substring by slicing and joining the string. If the resulting string is longer than the previously one $found I memorize it, and then I print.

PL/Perl Implementations

PWC 329 - Task 1 - PL/Perl Implementation

Same implementation of Raku: use an hash and iterate over all the matches of a regular expression to get all numbers.

CREATE OR REPLACE FUNCTION
pwc329.task1_plperl( text )
RETURNS SETOF int
AS $CODE$

   my ( $string ) = @_;
   die "Invalid string" unless( $string =~ / ^ [a-zA-Z0-9]+ $ /x );

   my $found = {};

   for ( $string =~ / [0-9]+ /xg ) {
        $found->{ $_ }++;
   }

   return [ sort keys $found->%* ];

$CODE$
LANGUAGE plperl;



PWC 329 - Task 2 - PL/Perl Implementation

Same implementation as in Raku, but with the Schwartzian Transform to grep the index of the last match of the $needle.

CREATE OR REPLACE FUNCTION
pwc329.task2_plperl( text )
RETURNS text
AS $CODE$

   my ( $string ) = @_;
   die "Invalid string" unless( $string =~ / ^ [a-zA-Z]+ $ /x );

   my $found;
   my @chars = split //, $string;
   for my $i ( 0 .. $#chars ) {
       my $current = $chars[ $i ];
       my $needle  = uc $current;
       $needle = lc $current if ( $current =~ /[A-Z]/ );
       my $index = 0;

       my $index = ( sort
	          map { $_->[ 1 ] }
	          grep { $_->[ 0 ] eq $needle }
	          map { [ $_, $index++ ] }  @chars[ $i .. $#chars ] )[ -1 ];
       my $match = join( '', @chars[ $i .. $index ] );
       $found = $match if ( ! $found || length( $found ) < length( $match ) );
   }

   return $found;

$CODE$
LANGUAGE plperl;



PostgreSQL Implementations

PWC 329 - Task 1 - PL/PgSQL Implementation

A single query does the trick!

CREATE OR REPLACE FUNCTION
pwc329.task1_plpgsql( s text )
RETURNS SETOF text
AS $CODE$
   SELECT distinct( v )
   FROM regexp_matches( s, '\d+', 'g' ) v
   ORDER BY 1
$CODE$
LANGUAGE sql;



PWC 329 - Task 2 - PL/PgSQL Implementation

Similar implementation to the Raku one, but longer…

CREATE OR REPLACE FUNCTION
pwc329.task2_plpgsql( s text )
RETURNS text
AS $CODE$
DECLARE
	current text;
	i int;
	chars text[];
	result text;
	tmp text;
	needle text;
BEGIN

	SELECT regexp_split_to_array( s, '' )
	INTO chars;

	result := '';

	FOR i in 1 .. length( s ) LOOP
	    current := chars[ i ];
	    needle  := upper( current );

	    IF current ~ '[A-Z]' THEN
	       needle := lower( current );
	    END IF;

	    tmp := '';
	    FOR j IN i .. length( s ) LOOP
	    	IF chars[ j ] = needle THEN
		   tmp := array_to_string( chars[ i : j ], '' );
		   IF length( tmp ) > length( result ) THEN
		      result := tmp;
		   END IF;
	       END IF;
	    END LOOP;

	END LOOP;

	RETURN result;
END
$CODE$
LANGUAGE plpgsql;



Java Implementations

PWC 329 - Task 1 - PostgreSQL PL/Java Implementation

Use a regular expression to match all the digits, and iterate over the matches to insert into an hash.

    public static final int[] task1_pljava( String string ) throws SQLException {
		logger.log( Level.INFO, "Entering pwc329.task1_pljava" );

		Map<Integer, Integer> numbers = new HashMap<Integer, Integer>();

		Pattern regexp = Pattern.compile( "\\d+" );
		Matcher engine = regexp.matcher( string );
		while ( engine.find() ) {
		    try {
				numbers.put( Integer.parseInt( engine.group() ), 1 );
		    } catch( Exception e ) { }
		}

		int result[] = new int[ numbers.size() ];
		int j = 0;
		for ( int i : numbers.keySet() )
		    result[ j++ ] = i;

		return result;
    }


PWC 329 - Task 2 - PostgreSQL PL/Java Implementation

A different approach: I use two boundaries begin and end to keep track of the longest substring for a match.

    public static final String task2_pljava( String string ) throws SQLException {
		logger.log( Level.INFO, "Entering pwc329.task2_pljava" );


		char chars[] = string.toCharArray();

		int begin = 0;
		int end   = 0;

		for ( int i = 0; i < chars.length; i++ ) {
		    char c = chars[ i ];
		    char n = Character.toUpperCase( c );
		    if ( Character.isUpperCase( c ) )
				n = Character.toLowerCase( c );

		    for ( int j = i + 1; j < chars.length; j++ )
				if ( chars[ j ] == n )
					if ( ( end - begin ) < ( j - i ) ) {
					begin = i;
					end   = j;
			    }
		}

		return string.substring( begin, end + 1 );
    }


Python Implementations

PWC 329 - Task 1 - Python Implementation

Use regular expression and get an implementation similar to PL/Perl.

import sys
import re

# task implementation
# the return value will be printed
def task_1( args ):
    string = args[ 0 ]

    numbers = []
    pattern = re.compile( r'\d+' )
    for i in pattern.findall( string ) :
        if not i in numbers :
            numbers.append( i )

    return numbers

# invoke the main without the command itself
if __name__ == '__main__':
    print( task_1( sys.argv[ 1: ] ) )



PWC 329 - Task 2 - Python Implementation

Same implementation as in PL/Java.

import sys

# task implementation
# the return value will be printed
def task_2( args ):
    string = args[ 0 ]

    begin = 0
    end   = 0

    for i in range( 0, len( string ) - 1 ) :
        current = string[ i ]
        needle = current.upper()
        if current.isupper() :
            needle = current.lower()

        for j in range( i + 1, len( string ) ) :
            print( " => ", string[ j ] )
            if string[ j ] == needle :
                if ( end - begin ) < ( j - i ) :
                    begin = i
                    end   = j

    return string[ begin : end + 1 ]


# invoke the main without the command itself
if __name__ == '__main__':
    print( task_2( sys.argv[ 1: ] ) )



The article Perl Weekly Challenge 329: mangling strings has been posted by Luca Ferrari on July 10, 2025