Perl Weekly Challenge 106
Task 2 seems like it would be difficult, but once again, Raku has built-in mechanisms to help us out!
Task 1: Maximum Gap
You are given an array of integers @N
.
Write a script to display the maximum difference between two successive elements once the array is sorted.
If the array contains only 1 element then display 0.
Examples
Input: @N = (2, 9, 3, 5)
Output: 4
Input: @N = (1, 3, 8, 2, 0)
Output: 5
Input: @N = (5)
Output: 0
Solution
See below for explanation and any implementation-specific comments.
sub challenge(@N where all(@N) ~~ Int) returns Int { # [1]
if @N.elems == 1 {
0;
} else {
my @sorted = @N.sort;
my @zipped = @sorted[0..*-1] Z @sorted[1..*]; # [2]
@zipped.map(-> ($a, $b) { abs($b - $a) }).max; # [3]
}
}
sub MAIN(*@N where all(@N) ~~ Int) {
say challenge(@N);
}
This program runs as such:
$ raku ch-1.raku 2 9 3 5
4
Explanation
This one is probably easier to walk through with an example. If @N
only has one element, we just return 0
, so we won’t discuss that path.
- We get this input:
@N = (2, 9, 3, 5)
. - We sort it so we have
@sorted = (2, 3, 5, 9)
. - We generate two lists: One of everything but the last element (
@sorted[0..*-1] = (2, 3, 5)
) and one of everything but the first element (@sorted[1..*] = (3, 5, 9)
). Then we zip them together so we have@zipped = ((2, 3), (3, 5), (5, 9))
. - For each pair, we find the difference between the numbers, so we end up with this:
(1, 2, 4)
. - Finally, we call
.max
and return the maximum difference, which is4
in this case.
Specific comments
- Raku has so many different sequence types (
Positional
,Array
,List
,Seq
, etc.) that I have not figured out a good way to parameterize positional inputs. This is the best I can come up with, but for long sequences, it is incredibly slow. - The “whatever star” is signaling the last index of the list, in this case. So we are simply generating a list containing everything but the last item zipped with a list containing everything but the last item (see above for example).
- In theory, we could have negative numbers in our input, so it is necessary to have the call to
abs
.
Task 2: Decimal String
You are given a numerator and denominator i.e. $N
and $D
.
Write a script to convert the fraction into decimal string. If the fractional part is recurring then put it in parentheses.
Input: $N = 1, $D = 3
Output: "0.(3)"
Input: $N = 1, $D = 2
Output: "0.5"
Input: $N = 5, $D = 66
Output: "0.0(75)"
Solution
See below for explanation and any implementation-specific comments.
sub challenge(Numeric $N, Numeric $D) returns Str { # [1]
my ($base, $repeating) = ($N / $D).base-repeating; # [2]
$repeating = $repeating eq '' ?? $repeating !! "\($repeating\)"; # [3]
$base ~ $repeating; # [4]
}
sub MAIN(Numeric $N, Numeric $D) {
say challenge($N, $D);
}
This program runs as such:
$ raku ch-2.raku 1 7
0.(142857)
Explanation
This looks suspiciously small for what seems like a complex problem. All the logic happens in base-repeating
(which I will detail below). All we have to do is supply the $N
and D
, and the rest of the code is just for formatting!
Specific Comments
- In theory someone could pass in arbitrary decimal numbers, so we accept
Numeric
instead of justInt
. base-repeating
operates on aRational
, which is Raku’s way of saying fraction. It splits the fraction into pieces – the base and the repeating portion. For something like5 / 2
, it would return('2.5', '')
. For something like1 / 3
, we would get'0.', '3')
. Once we have that, we just have to format like the question asks.- If the repeated section is empty, we want to just leave it alone. Otherwise, we want to wrap it in parentheses as the challenge states.
- We could also have written
"$base$repeating"
to concatenate the pieces, but that is harder to read, so I used the string concatenation operator (~
).
Final Thoughts
I don’t know whether or not to consider it cheating when Raku has all the cool built-ins. I guess we’re just using the tools we are provided, but it feels too easy. 😅