I’ve been doing the Weekly
Challenges. The
latest
involved adding thousands separators and testing arrays. (Note that
this ends today.)
Task 1: Thousand Separator
You are given a positive integer, $int.
Write a script to add thousand separator, , and return as string.
There's a recipe for this in the Perl Cookbook, of course, involving
reversing the stringified number and applying a regexp. I thought I'd
do it without the reverse. Scala:
def thousandseparator(a: Int): String = {
var out = ""
Clearly I have to start with the stringified number.
val s = a.toString
Since it's a positive integer, every character is a significant digit
for the thousands grouping. I calculate the digit offset such that a comma
should come before this digit in the output.
val offset = 2 - ((s.length + 2) % 3)
Then run though the string, prepending a digit if we've hit the offset
and this isn't the first character.
for ((d, i) <- s.toList.zipWithIndex) {
if (i > 0 && (i + offset) % 3 == 0) {
out += ','
}
out += d
}
out
}
Task 2: Mountain Array
You are given an array of integers, @ints.
Write a script to return true if the given array is a valid mountain array.
An array is mountain if and only if:
- arr.length >= 3
and
- There exists some i with 0 < i < arr.length - 1 such that:
arr[0] < arr[1] < ... < arr[i - 1] < arr[i]
arr[i] > arr[i + 1] > ... > arr[arr.length - 1]
As I see it, this is a job for a state machine. In state 0, we have
not yet seen any pairs of numbers, or we see an ascending pair and
move to state 1; in state 1, the numbers are going up, or we see a
descending pair and move to state 2; in state 2, the numbers are going
down, or we reach the end.
In Raku:
sub mountainarray(@a) {
my $state = 0;
Look at each pair of numbers in turn.
for @a.rotor(2 => -1) -> @b {
Ascending pair.
if (@b[1] > @b[0]) {
If we were in state 0 or 1, we're now in state 1.
if ($state == 0 || $state == 1) {
$state = 1;
Otherwise (e.g. we were in state 2) the test fails.
} else {
return False;
}
Descending pair.
} elsif (@b[1] < @b[0]) {
If we were in state 1 or 2, we're now in state 2.
if ($state == 1 || $state == 2) {
$state = 2;
Otherwise (e.g. we were in state 0) the test fails.
} else {
return False;
}
The pair is neither ascending nor descending. The test fails.
} else {
return False;
}
}
We've got to the end, but we must have reached state 2 (at least one
ascending pair followed by at least one descending pair) in order for
the sequence to pass.
$state == 2
}
Full code on
github.