Perl Weekly Challenge: Week 355
Challenge 1:
Thousand Seperator
You are given a positive integer,
$int.Write a script to add thousand separator, , and return as string.
Example 1
Input: $int = 123
Output: "123"
Example 2
Input: $int = 1234
Output: "1,234"
Example 3
Input: $int = 1000000
Output: "1,000,000"
Example 4
Input: $int = 1
Output: "1"
Example 5
Input: $int = 12345
Output: "12,345"
I knew I could solve this one as a one-liner but it turned out to be a little bit more complicated than I originally anticipated.
We start by assigning the first command-line argument (our input) to $_. As well as making the length
of the variable name shorter, $_ is default target of many methods. Both of these are helpful in reducing
the length of a one-liner.
The key to this solution is the .comb(3) method which will split $_ into a sequence of three character strings. If we put a , between them we will be done right? Unfortunately, comb starts from the
left of the string so for e.g. example 2, we would get "123,4" instead of "1,234" which is what we actually want.
The trick is to pad the string enough to make its' length an even multiple of three. That's what .subst(/^/, (0 x 3 - .chars % 3)) does; it adds zeros's as padding. If the string is already a multiple of 3, it will add three zeros which is unnecessary but harmless so to avoid complication I left it as is.
Now we can apply .comb() and then .join() the sub-lists with commas.
The last stage is to remove the leading zero padding (and a leading comma if there is one) with subst() and
print the result with .say().
$_= @*ARGS[0]; .subst(/^/, (0 x 3 - .chars % 3)).comb(3).join(q{,}).subst(/^0+\,*/, "").say
The Perl version is not a one-liner but it is, I think, a little clearer to understand.
$int =~ s/^/0 x (3 - (length $int) % 3)/e;
$int = join q{,}, ($int =~ /(.{3})/g);
$int =~s/^0+,*//;
say $int;
Challenge 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:
1) arr.length >= 3
and
2) 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]
Example 1
Input: @ints = (1, 2, 3, 4, 5)
Output: false
Example 2
Input: @ints = (0, 2, 4, 6, 4, 2, 0)
Output: true
Example 3
Input: @ints = (5, 4, 3, 2, 1)
Output: false
Example 4
Input: @ints = (1, 3, 5, 5, 4, 2)
Output: false
Example 5
Input: @ints = (1, 3, 2)
Output: true
The MAIN() function is very simple:
say isMountain(@ints);
The isMountain() function is where all the work happens. It takes a list of integers
and returns True or False if they represent a mountain array or not.
sub isMountain(@ints) {
As the spec suggests, if @ints has less than 3 elements it is not a mountain array
so we can immediately return False.
if @ints.elems < 3 {
return False;
}
We will start from the beginning of @ints (index 0 which will be assigned to $i.)
my $i = 0;
As we "climb up" @ints (meaning each succeeding value is greater than the preceding one,) we
increment $1. We also check we are not at the end of @ints.
while $i < @ints.end && @ints[$i] < @ints[$i + 1] {
$i++;
}
When we have stopped climbing, we are at the "peak" of the mountain. We check to see
that this is not the end of @ints or the beginning (which could happen if the second element
was smaller than the first so we didn't climb at all.) In those cases, this is not a mountain array
so we return False.
if $i == 0 || $i == @ints.end {
return False;
}
Now we begin "climbing down". Once again we should check we aren't at the end of @ints.
while $i < @ints.end && @ints[$i] > @ints[$i + 1] {
$i++;
}
By now $i should have equalled the index of the last element in @ints. This might
not be True if e.g. after climbing down we may have begun climbing up again.
return $i == @ints.end;
}
This is the Perl version.
sub isMountain(@ints) {
if (@ints < 3) {
return false;
}
my $i = 0;
while ($i < $#ints && $ints[$i] < $ints[$i + 1]) {
$i++;
}
if ($i == 0 || $i == $#ints) {
return false;
}
while ($i < $#ints && $ints[$i] > $ints[$i + 1]) {
$i++;
}
return $i == $#ints;
}
say isMountain(@ARGV) ? 'true' : 'false';