This is my response to The Weekly Challenge #322.
Input: $str = "ABC-D-E-F", $i = 3
Output: "ABC-DEF"
Example 2:
Input: $str = "A-BC-D-E", $i = 2
Output: "A-BC-DE"
Example 3:
Input: $str = "-A-B-CD-E", $i = 4
Output: "A-BCDE"
File: string-format
#! /usr/bin/env raku
unit sub MAIN ($str is copy where $str.chars > 0, # [1]
Int $i where $i > 0, # [2]
:v(:$verbose));
$str ~~ s:g/"-"//; # [3]
say ": Dash free: $str" if $verbose;
my @new; # [4]
my $size = $str.chars % $i; # [5]
if $size # [6]
{
my $add = $str.substr(0, $size); # [7]
$str = $str.substr($size); # [8]
@new.push: $add; # [9]
say ": Short part: $add" if $verbose;
}
while $str.chars # [10]
{
my $add = $str.substr(0, $i); # [10a]
$str = $str.substr($i); # [10b]
@new.push: $add; # [10c3]
say ": Full size part: $add" if $verbose;
}
say @new.join("-"); # [11]
[1] A string with at least one character. Note the is copy
trait, so that we can change the value later on (in [8] and {12]).
See docs.raku.org/type/Parameter#method_copy for more information about is copy
.
[2] Ensure a positive integer.
[3] Get rid of any dashes with the in-place substitution operator s///
.
See
docs.raku.org/language/operators#s///_in-place_substitution
for more information about s///
.
[4] The partial strings, that we are to glue together with dashes later on, will end up here.
[5] How many characters do we have left when we remove $int
characters as many times as
possible?
[6] If any
[7] • Get (copy) that number of character from the start of the string.
[8] • And remove them from the string itself.
[9] • Add the partial string toi the result.
[10] Now the same (as [7,7,9]), in a loop, as long as we have more characters to do. But this time,
we take $int
characters at a time.
[11] Join the partial strings up with dashes, and print the result.
Running it:
$ ./string-format "A-BC-D-E" 2
A-BC-DE
$ ./string-format "A-BCDE" 4
A-BCDE
$ ./string-format "A-BCDEF" 4
AB-CDEF
Looking good.
With verbose mode:
$ ./string-format -v "A-BC-D-E" 2
: Dash free: ABCDE
: Short part: A
: Full size part: BC
: Full size part: DE
A-BC-DE
$ ./string-format -v "A-BCDE" 4
: Dash free: ABCDE
: Short part: A
: Full size part: BCDE
A-BCDE
$ ./string-format -v "A-BCDEF" 4
: Dash free: ABCDEF
: Short part: AB
: Full size part: CDEF
AB-CDEF
The program works, but is a bit cumbersome. We can make it much neater if we start at the end
(pun intended), gobbling up $int
number of characters at a time, and finally adding the
remainder - if any.
#! /usr/bin/env raku
unit sub MAIN ($str is copy where $str.chars > 0,
Int $i where $i > 0,
:v(:$verbose));
$str ~~ s:g/"-"//;
say ": Dash free: $str" if $verbose;
my @new;
while $str.chars >= $i # [1]
{
my $add = $str.substr($str.chars - $i); # [2]
$str = $str.substr(0, $str.chars - $i); # [3]
@new.unshift: $add; # [4]
say ": Full size part: $add" if $verbose;
}
if $str.chars # [5]
{
@new.unshift: $str; # [5a]
say ": Short part: $str" if $verbose;
}
say @new.join("-"); # [6]
[1] As long as we $int
or more characters left,
[2] • Get $int
number of characters from the end of the string.
[3] • Remove those characters from the string.
[4] • Add the substring to the front (or left hand side) of the
array, with unshift
.
See docs.raku.org/routine/unshift for more information about unshift
.
[5] The final part is either non-existent or has fever character than $int
.
Add that string, also to the front of the array, if it exists.
[6] Note that the array order is ok, as we used unshift
instead of push
.
So we can just join them without further ado. Or rather with dashes. (Much ado
about dashes. Now that could have been the title for this artoicle. Alas,
Yorick...)
Running it gives the expected result, but verbose mode has the substring order reversed.
$ ./string-format-reverse -v "A-BC-D-E" 2
: Dash free: ABCDE
: Full size part: DE
: Full size part: BC
: Short part: A
A-BC-DE
$ ./string-format-reverse -v "A-BCDE" 4
: Dash free: ABCDE
: Full size part: BCDE
: Short part: A
A-BCDE
$ ./string-format-reverse -v "A-BCDEF" 4
: Dash free: ABCDEF
: Full size part: CDEF
: Short part: AB
AB-CDEF
Input: @ints = (55, 22, 44, 33)
Output: (4, 1, 3, 2)
Example 2:
Input: @ints = (10, 10, 10)
Output: (1, 1, 1)
Example 3:
Input: @ints = (5, 1, 1, 4, 3)
Output: (4, 1, 1, 3, 2)
#! /usr/bin/env raku
unit sub MAIN (*@ints where @ints.elems > 1 # [1]
&& all(@ints) ~~ Int, # [1a]
:v(:$verbose));
my @sorted = @ints.sort.squish; # [2]
my %rank; # [3]
my $rank = 0; # [5a]
for @sorted -> $val # [4]
{
$rank++; # [5]
%rank{$val} = $rank; # [6]
say ": Int $val has rank $rank" if $verbose;
}
my @rank = @ints.map({ %rank{$_} }); # [7]
say "({ @rank.join(", ") })"; # [8]
[1] A slurpy array with at least 2 elements, all of which must be integers.
[2]
Get a sorted version of the input, without duplicates. We can
use the more efficient squish
instead of unqiue
as the values are
sorted.
See docs.raku.org/routine/squish for more information about squish
.
See docs.raku.org/routine/unqiue for more information about unqiue
.
[3] The rank mpping, from value to rank, will end up here.
[4] Iterate over the sorted (and unique) values.
[5] Increase the rank position, initialised in [5a].
[6] Note the rank for the current value.
[8] Use map
to swap the values in the original array with the rank
value for each value.
See docs.raku.org/routine/map for more information about map
.
[8] Pretty print the result.
Running it:
$ ./rank-array 55 22 44 33
(4, 1, 3, 2)
$ ./rank-array 10 10 10
(1, 1, 1)
$ ./rank-array 5 1 1 4 3
(4, 1, 1, 3, 2)
Looking good.
With verbose mode:
$ ./rank-array -v 55 22 44 33
: Int 22 has rank 1
: Int 33 has rank 2
: Int 44 has rank 3
: Int 55 has rank 4
(4, 1, 3, 2)
$ ./rank-array -v 10 10 10
: Int 10 has rank 1
(1, 1, 1)
$ ./rank-array -v 5 1 1 4 3
: Int 1 has rank 1
: Int 3 has rank 2
: Int 4 has rank 3
: Int 5 has rank 4
(4, 1, 1, 3, 2)
And that's it.