This is my response to The Weekly Challenge #318.
Input: $str = "abccccd"
Output: "cccc"
Example 2:
Input: $str = "aaabcddddeefff"
Output: "aaa", "dddd", "fff"
Example 3:
Input: $str = "abcdd"
Output: ""
Note that the challenge asks for the position of the groups, but the examples give the groups themselves. I'll follow the examples.
File: group-position
#! /usr/bin/env raku
unit sub MAIN ($str where $str ~~ /^ <[a..z]>+ $/, # [1]
:v(:$verbose));
sub grouped ($str, :$verbose) # [2]
{
my @parts = gather
{
my $curr;
my $count = 0;
for $str.comb -> $char
{
if $count && $curr ne $char
{
take $curr x $count;
$count = 0;
}
$curr = $char;
$count++;
}
take $curr x $count;
}
say ": Grouped '$str' as { @parts.raku }" if $verbose;
return @parts;
}
say grouped($str, :$verbose).grep(*.chars > 2).map('"' ~ * ~ '"').join(", ") || '""';
## 3 ##################### # 4 ############# # 5 ############### # 6 ##### # 7 ###
[1] Lowercase english letters only, with at least one.
[2] This procedure has been copied from my "broken-keys" program in Challenge 313 Reverse Broken with Raku. I have changed the verbose flag to be a named argument passed to the procedure this time, instead of the former global variable.
[3] Call "grouped" to to do the grouping, including verbose mode.
[4] Only keep elements in the list that have more than two characters.
[5] Print quotes around the strings.
[6] Add commas between the strings.
[7] No elements? Print a pair of empty quotes.
Running it:
$ ./group-position abccccd
"cccc"
$ ./group-position aaabcddddeefff
"aaa", "dddd", "fff"
$ ./group-position abcdd
""
Looking good.
With verbose mode:
$ ./group-position -v abccccd
: Grouped 'abccccd' as ["a", "b", "cccc", "d"]
"cccc"
$ ./group-position -v aaabcddddeefff
: Grouped 'aaabcddddeefff' as ["aaa", "b", "c", "dddd", "ee", "fff"]
"aaa", "dddd", "fff"
$ ./group-position -v abcdd
: Grouped 'abcdd' as ["a", "b", "c", "dd"]
""
Input: @source = (3, 2, 1, 4)
@target = (1, 2, 3, 4)
Output: true
Reverse elements: 0-2
Example 2:
Input: @source = (1, 3, 4)
@target = (4, 1, 3)
Output: false
Example 3:
Input: @source = (2)
@target = (2)
Output: true
#! /usr/bin/env raku
subset NUMWORD where * ~~ /^<[0..9 \s]>+$/; # [1]
unit sub MAIN (NUMWORD $source, # [1a]
NUMWORD $target, # [1b]
:v(:$verbose));
my @source = $source.words; # [2]
my @target = $target.words; # [2a]
die "Not the same elements"
unless @source.sort eqv @target.sort; # [3]
if @source eqv @target # [4]
{
say 'true'; # [4a]
exit; # [4b]
}
my $end = @target.end; # [5]
for ^$end -> $i # [6]
{
for $i + 1 .. $end -> $j # [7]
{
my @copy = @source.clone; # [8]
@copy[$i .. $j] = @copy[$i .. $j].reverse; # [9]
say ": ({ @source.join(",") }) Reverse $i..$j -> ({ @copy.join(",") })"
if $verbose;
if @copy eqv @target # [10]
{
say 'true'; # [10a]
exit; # [10b]
}
}
}
say 'false'; # [11]
[1] A custom type set up with subset
for the two arguments
([1a] and [1b]), allowing digits and spaces only. We specify the two arrays as
two space separated strings.
See docs.raku.org/routine/subset for more information about subset
.
[2] Use words
to split the string into an array of
integers.
See docs.raku.org/routine/words for more information about words
.
[3] Ensure that that the source and target have the same content,
i.e. are identical after sorting them. Note the use of the equivalence operator
eqv
to check that the structures contain the same values.
See docs.raku.org/routine/eqv for more information about eqv
.
[4] Are the source and target identical? If so, we can reverse a one-element subarray thus keeping it unchanged (as done in the third example). Success. Say so [4a] and be done [4b].
[5] The highest index in the array(s), gotten with end
.
See docs.raku.org/routine/end for more information about end
.
[6] Iterate over the starting position for all possible
subarrays, from index 0 to one less than the end (with the upto operator ^
).
See
docs.raku.org/routine/^
for more information about the Upto Operator ^
.
[7] Iterate over the ending positions. Starting at one after the starting position and continuing to the end.
[8] Get a copy of the source array with clone
.
See docs.raku.org/routine/clone for more information about clone
.
[9] Swap the subarray with the reversed version, using array slices.
Note that it is possible to optimize the program by skipping the
swapping and reversal if the non-swapped part(s) are different from the target.
This could typically be as last if ...
and/or next if ...
just after
the two for
statements (in [6] and [7]). It is anybody's guess if
this would speed up the program. I fear not, for the short arrays given in the
excmples, and choose to let sleeping dogs lie.
[10] Have we gotten the target? If so, say so [10a] and exit [10b].
[11] Failure to succeed means failure. Say so.
Running it:
$ ./reverse-equals "3 2 1 4" "1 2 3 4"
true
$ ./reverse-equals "1 3 4" "4 1 3"
false
$ ./reverse-equals 2 2
true
Looking good.
With verbose mode:
$ ./reverse-equals -v "3 2 1 4" "1 2 3 4"
: (3,2,1,4) Reverse 0..1 -> (2,3,1,4)
: (3,2,1,4) Reverse 0..2 -> (1,2,3,4)
true
$ ./reverse-equals -v "1 3 4" "4 1 3"
: (1,3,4) Reverse 0..1 -> (3,1,4)
: (1,3,4) Reverse 0..2 -> (4,3,1)
: (1,3,4) Reverse 1..2 -> (1,4,3)
false
$ ./reverse-equals -v 2 2
true
And that's it.