PERL WEEKLY CHALLENGE – 063

This is my 33rd week participating into the weekly challenge.


TASK #1 › Last Word

Submitted by: Mohammad S Anwar
Reviewed by: Ryan Thompson

Define sub last_word($string, $regexp) that returns the last word matching $regexp found in the given string, or undef if the string does not contain a word matching $regexp.

For this challenge, a “word” is defined as any character sequence consisting of non-whitespace characters (\S) only. That means punctuation and other symbols are part of the word.

The $regexp is a regular expression. Take care that the regexp can only match individual words! See the Examples for one way this can break if you are not careful.

Examples

last_word('  hello world',                qr/[ea]l/);      # 'hello'
last_word("Don't match too much, Chet!",  qr/ch.t/i);      # 'Chet!'
last_word("spaces in regexp won't match", qr/in re/);      #  undef
last_word( join(' ', 1..1e6),             qr/^(3.*?){3}/); # '399933'

For this challenge I just split the string into words and reversed the array. Then processed each word until a match with the regex was found, or if no match was found return undef

Perl 5 solution

#!/usr/bin/perl
# Test: ./ch-1.pl
use strict;
use warnings;
use feature qw /say/;
use Test::More;

is (last_word('  hello world',                qr/[ea]l/),      'hello',  'Hello world');
is (last_word("Don't match too much, Chet!",  qr/ch.t/i),      'Chet!',  'Chet');
is (last_word("spaces in regexp won't match", qr/in re/),      undef,    'Undef');
is (last_word( join(' ', 1..1e6),             qr/^(3.*?){3}/), '399933', 'Numbers');

done_testing();

sub last_word {
	my ($string, $regex) = @_;
	my @words = reverse (split ('\s', $string));

	for my $word (@words) {
		return $word
			if ($word =~ /$regex/);
	}

	return undef;
}

Output: perl ./ch-1.pl

ok 1 - Hello world
ok 2 - Chet
ok 3 - Undef
ok 4 - Numbers

Raku solution

# Test: perl6 ch-1.p6
use Test;

sub MAIN() {
	is last_word('  hello world',                rx/[e|a]l/),      'hello',  'Hello world';
	is last_word("Don't match too much, Chet!",  rx:i/ch.t/),      'Chet!',  'Chet';
	is last_word("spaces in regexp won't match", rx/"in re"/),      Nil,     'Undef';
	is last_word( join(' ', 1..1e6),            rx/^^(3.*?)**3/), '399933',  'Numbers';
	done-testing;
}

# Check if the email is unique
sub last_word(Str $string, $regex)  {
	my @words = $string.split(/\s/).reverse;

	for (@words) -> $word {
		return $word
			if ($word ~~ $regex);
	}

	return Nil;
}

Output perl6 ch-1.p6

ok 1 - Hello world
ok 2 - Chet
ok 3 - Undef
ok 4 - Numbers
1..4

TASK #2 › Rotate String

Submitted by: Mohammad S Anwar
Reviewed by: Ryan Thompson

Given a word made up of an arbitrary number of x and y characters, that word can be rotated as follows: For the ith rotation (starting at i = 1), i % length(word) characters are moved from the front of the string to the end. Thus, for the string xyxx, the initial (i = 1) % 4 = 1 character (x) is moved to the end, forming yxxx. On the second rotation, (i = 2) % 4 = 2 characters (yx) are moved to the end, forming xxyx, and so on. See below for a complete example.

Your task is to write a function that takes a string of xs and ys and returns the maximum non-zero number of rotations required to obtain the original string. You may show the individual rotations if you wish, but that is not required.

Example

Input: $word = 'xyxx';

  • Rotation 1: you get yxxx by moving x to the end.
  • Rotation 2: you get xxyx by moving yx to the end.
  • Rotation 3: you get xxxy by moving xxy to the end.
  • Rotation 4: you get xxxy by moving nothing as 4 % length(xyxx) == 0.
  • Rotation 5: you get xxyx by moving x to the end.
  • Rotation 6: you get yxxx by moving xx to the end.
  • Rotation 7: you get xyxx by moving yxxx to the end which is same as the given word.

Output: 7


For this challenge I just created a copy of the word, and used the substr method to create the new rotated word

Perl 5 solution

#!/usr/bin/perl
# Test: ./ch-2.pl
use strict;
use warnings;
use feature qw /say/;

say rotate('xyxx') . ' rotations';

sub rotate {
	my $word = shift;
	my $copy = $word;

	# The total length of the word
	my $total_word_length = length($word);
	my $i = 0;

	do {
		$i++;
		my $position = $i % $total_word_length;
		my $length   = $total_word_length - $position;

		$copy = (substr $copy, $position, $length) .
		        (substr $copy, 0, $position);

		say "Rotation $i: $copy";
	} while ($copy ne $word);

	return $i;
}

Output ./ch-2.pl

Rotation 1: yxxx
Rotation 2: xxyx
Rotation 3: xxxy
Rotation 4: xxxy
Rotation 5: xxyx
Rotation 6: yxxx
Rotation 7: xyxx
7 rotations

Raku solution

# Test: perl6 ch-2.p6

sub MAIN() {
	say rotate('xyxx') ~ ' rotations';
}

sub rotate(Str $word) {
	my $copy = $word;

	# The total length of the word
	my $total_word_length = $word.chars;
	my $i = 0;

	repeat {
		$i++;
		my $position = $i % $total_word_length;
		my $length   = $total_word_length - $position;

		$copy = $copy.substr($position, $length) ~
		        $copy.substr(0, $position);

		say "Rotation $i: $copy";
	} while ($copy ne $word);

	return $i;
}

Output perl6 ch-2.p6

Rotation 1: yxxx
Rotation 2: xxyx
Rotation 3: xxxy
Rotation 4: xxxy
Rotation 5: xxyx
Rotation 6: yxxx
Rotation 7: xyxx
7 rotations

One thought on “PERL WEEKLY CHALLENGE – 063

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s