This is my response to The Weekly Challenge #329.
Input: $str = "the1weekly2challenge2"
Output: 1, 2
2 is appeared twice, so we count it one only.
Example 2:
Input: $str = "go21od1lu5c7k"
Output: 21, 1, 5, 7
Example 3:
Input: $str = "4p3e2r1l"
Output: 4, 3, 2, 1
I cannot see any reason to replace the non-digit characters with a space, as the spaces are not used for anything later on. The second step would then be splitting the string on spaces, but we can skip this trip to space by just splitting the original string on the lowercase letters.
File: counter-integers
#! /usr/bin/env raku
unit sub MAIN ($str where $str ~~ /^<[ a..z 0 .. 9 ]>+$/, # [1]
:v(:$verbose));
my @ints = $str.split(/<[a..z]>+/).grep: * ~~ /\d+/; # [2]
say ": Integers: { @ints.join(", ") }" if $verbose;
say @ints.unique.join(", "); # [3]
[1] Ensure lowercase letters and digit only, with at least one of them.
[2] The letters are ignoreable (i.e. junk), so we can split on them to get a
list of consecutive digits. The trailing grep
is there to get rid of
a leading (if the string started with a digit) or trailing (if it ended with
a digit) empty element.
[3] Get rid of duplicate integers with unique
, and print
the result.
See docs.raku.org/routine/unique for more information about unique
.
Running it:
$ ./counter-integers the1weekly2challenge2
1, 2
$ ./counter-integers go21od1lu5c7k
21, 1, 5, 7
$ ./counter-integers 4p3e2r1l
4, 3, 2, 1
Looking good.
With verbose mode:
$ ./counter-integers -v the1weekly2challenge2
: Integers: 1, 2, 2
1, 2
$ ./counter-integers -v go21od1lu5c7k
: Integers: 21, 1, 5, 7
21, 1, 5, 7
$ ./counter-integers -v 4p3e2r1l
: Integers: 4, 3, 2, 1
4, 3, 2, 1
Input: $str = "YaaAho"
Output: "aaA"
Example 2:
Input: $str = "cC"
Output: "cC"
Example 3:
Input: $str = "A"
Output: ""
No nice string found.
#! /usr/bin/env raku
unit sub MAIN ($str where $str ~~ /^<[ a..z A .. Z ]>+$/, # [1]
:v(:$verbose));
my @str = $str.comb; # [2]
my $current = @str.shift; # [3]
my @nice; # [4]
while @str.elems # [5]
{
if @str[0].lc eq $current.substr(0,1).lc # [6]
{
$current ~= @str.shift; # [7]
}
else # [8]
{
@nice.push: $current if $current.comb.unique.elems == 2; # [9]
$current = @str.shift; # [10]
}
}
@nice.push($current) if $current.comb.unique.elems == 2; # [11]
say ": Nice: { @nice.join(", ") }" if $verbose;
@nice .= sort({ $^b.chars <=> $^a.chars }); # [12]
say ": Nice by length: { @nice.join(", ") }" if $verbose;
say @nice.first // ""; # [13]
[1] Ensure lower- and uppercase letters only; at least one.
[2] Get a list of single characters.
[3] The current character.
[4] The nice strings will end up here.
[5] As long as we have more letters to process.
[6] Is the lowercase version of the next letter (@str[0].lc
)
identical to the current letter (which may be a string, so we get
the first one with substr
).
[7] • Add the next letter to the current string (and remove it from the list).
[8] If not,
[9] • We are starting on a new nice string. Add the previous one to the list, but only if it has both a lower- and an uppercase letter. This is done by checking that we have two unique characters (as they are identical if we ignore the case).
[10] • Prepare for the next iteration.
[11] No more unfinished letters, but we have to add a final nice string.
[12] Sort the list of nice strings by their lengths; with the longest first.
[13] Print the longest (i.e. first
in the list),
and default to an empty string in the absence of any nice strings.
See docs.raku.org/routine/first for more information about first
.
Running it:
$ ./nice-string YaaAho
aaA
$ ./nice-string cC
cC
9 $ ./nice-string A
Looking good.
With verbose mode:
$ ./nice-string -v YaaAho
: Nice: aaA
: Nice by length: aaA
aaA
$ ./nice-string -v cC
: Nice: cC
: Nice by length: cC
cC
9 $ ./nice-string -v A
: Nice:
: Nice by length:
And that's it.