I wrote the solutions two days ago, but couldn’t think of anything to write about it, and now it’s Mother’s Day and if I don’t post this now, it will be too late. And I promised myself I’d keep up with these for my mom, so, without any music or other commentary, let’s look at Perl Weekly Challenge 320.
Task 1: Maximum Count
You are given an array of integers.
Write a script to return the maximum between the number of positive and negative integers. Zero is neither positive nor negative.
Example 1
Input: @ints = (-3, -2, -1, 1, 2, 3)
Output: 3
There are 3 positive integers.
There are 3 negative integers.
The maximum between 3 and 3 is 3.
Example 2
Input: @ints = (-2, -1, 0, 0, 1)
Output: 2
There are 1 positive integers.
There are 2 negative integers.
The maximum between 2 and 1 is 2.
Example 3
Input: @ints = (1, 2, 3, 4)
Output: 4
There are 4 positive integers.
There are 0 negative integers.
The maximum between 4 and 0 is 4.
Approach
This doesn’t really take much explanation: we keep two counts, one for positive integers, one for negative integers. We make one pass over the array, incrementing the appropriate counter, then we return the max of the two counters.
Raku
Raku has a built-in max
method on the Any type, so we can just use that to get the maximum value.
sub maxCount(@ints) {
my ($pos, $neg) = (0, 0);
for @ints -> $int {
if ($int > 0) {
$pos++;
}
elsif ($int < 0) {
$neg++;
}
}
my $max = ($pos, $neg).max;
my $explain =
"There are $pos positive integers.\n" ~
"There are $neg negative integers.\n" ~
"The maximum between $pos and $neg is $max.";
return ($max, $explain);
}
View the entire Raku script for this task on GitHub.
$ raku/ch-1.raku
Example 1:
Input: @ints = (-3, -2, -1, 1, 2, 3)
Output: 3
There are 3 positive integers.
There are 3 negative integers.
The maximum between 3 and 3 is 3.
Example 2:
Input: @ints = (-2, -1, 0, 0, 1)
Output: 2
There are 1 positive integers.
There are 2 negative integers.
The maximum between 1 and 2 is 2.
Example 3:
Input: @ints = (1, 2, 3, 4)
Output: 4
There are 4 positive integers.
There are 0 negative integers.
The maximum between 4 and 0 is 4.
Perl
Whereas in Perl, we have to import it from a list module (here, I’m using List::AllUtils
).
use List::AllUtils qw( max );
sub maxCount(@ints) {
my ($pos, $neg) = (0, 0);
foreach my $int ( @ints ) {
if ($int > 0) {
$pos++;
}
elsif ($int < 0) {
$neg++;
}
}
my $max = max($pos, $neg);
my $explain =
"There are $pos positive integers.\n" .
"There are $neg negative integers.\n" .
"The maximum between $pos and $neg is $max.";
return ($max, $explain);
}
View the entire Perl script for this task on GitHub.
Python
But, of course, Python has a built-in max
function.
def maxCount(ints):
pos = 0
neg = 0
for int in ints:
if int > 0:
pos += 1
elif int < 0:
neg +=1
max_count = max(pos, neg)
explain = (
f"There are {pos} positive integers.\n" +
f"There are {neg} negative integers.\n" +
f"The maximum between {pos} and {neg} is {max_count}."
)
return max_count, explain
View the entire Python script for this task on GitHub.
Elixir
This time, instead of defining the function I’m using in Enum.map_reduce/3
anonymously in the function call, I decided to define a private function so the code would look cleaner.
defp counter(int, {pos, neg}) do
{pos, neg} = cond do
int > 0 -> {pos+1, neg }
int < 0 -> {pos, neg+1}
true -> {pos, neg }
end
{int, {pos, neg}}
end
def maxCount(ints) do
{_, {pos, neg}} = Enum.map_reduce(ints, {0, 0}, &counter/2)
max = Enum.max([pos, neg])
{
max,
"There are #{pos} positive integers.\n" <>
"There are #{neg} negative integers.\n" <>
"The maximum between #{pos} and #{neg} is #{max}."
}
end
View the entire Elixir script for this task on GitHub.
Task 2: Sum Difference
You are given an array of positive integers.
Write a script to return the absolute difference between digit sum and element sum of the given array.
Example 1
Input: @ints = (1, 23, 4, 5)
Output: 18
Element sum: 1 + 23 + 4 + 5 => 33
Digit sum: 1 + 2 + 3 + 4 + 5 => 15
Absolute difference: | 33 - 15 | => 18
Example 2
Input: @ints = (1, 2, 3, 4, 5)
Output: 0
Element sum: 1 + 2 + 3 + 4 + 5 => 15
Digit sum: 1 + 2 + 3 + 4 + 5 => 15
Absolute difference: | 15 - 15 | => 0
Example 3
Input: @ints = (1, 2, 34)
Output: 27
Element sum: 1 + 2 + 34 => 37
Digit sum: 1 + 2 + 3 + 4 => 10
Absolute difference: | 37 - 10 | => 27
Approach
We’ve processed numbers as their individual digits before (back in PWC 230 I used modulo division to pull each digit out of the number), but by far the easiest way is to convert the numbers to strings, concatenate them into one long string, and then split out the string into individual characters.
Raku
In Raku, like Perl, casting numbers to strings and back again is automatic, so this was an opportunity to play with the sum
and join
routines on the List
object, and the comb
routine on the Str
object.
sub subDiff(@ints) {
my $element_sum = @ints.sum;
my $digit_sum = @ints.join.comb.sum;
my $abs_diff = ($element_sum - $digit_sum).abs;
my $int_list = @ints.join(" + ");
my $digit_list = @ints.join.comb.join(" + ");
return (
$abs_diff,
"Element sum: $int_list => $element_sum\n" ~
"Digit sum: $digit_list => $digit_sum\n" ~
"Absolute difference: | $element_sum - $digit_sum | " ~
"=> $abs_diff"
);
}
View the entire Raku script for this task on GitHub.
$ raku/ch-2.raku
Example 1:
Input: @ints = (1, 23, 4, 5)
Output: 18
Element sum: 1 + 23 + 4 + 5 => 33
Digit sum: 1 + 2 + 3 + 4 + 5 => 15
Absolute difference: | 33 - 15 | => 18
Example 2:
Input: @ints = (1, 2, 3, 4, 5)
Output: 0
Element sum: 1 + 2 + 3 + 4 + 5 => 15
Digit sum: 1 + 2 + 3 + 4 + 5 => 15
Absolute difference: | 15 - 15 | => 0
Example 3:
Input: @ints = (1, 2, 34)
Output: 27
Element sum: 1 + 2 + 34 => 37
Digit sum: 1 + 2 + 3 + 4 => 10
Absolute difference: | 37 - 10 | => 27
Perl
In Perl, all we need to pull in from List::AllUtils
is sum
.
use List::AllUtils qw( sum );
sub subDiff(@ints) {
my $element_sum = sum @ints;
my $digit_sum = sum split(//, join(q{}, @ints));
my $abs_diff = abs($element_sum - $digit_sum);
my $int_list = join " + ", @ints;
my $digit_list = join " + ", split(//, join(q{}, @ints));
return (
$abs_diff,
"Element sum: $int_list => $element_sum\n" .
"Digit sum: $digit_list => $digit_sum\n" .
"Absolute difference: | $element_sum - $digit_sum | " .
"=> $abs_diff"
);
}
View the entire Perl script for this task on GitHub.
Python
Because joining lists of integers is a little involved in Python, I wrote myself a little helper function to abstract out all the work. Also, converting ints
into a list of characters of the individual digits was fairly easy once I had that helper function ( list(int_join("", ints))
), but I had to loop over the list of characters individually to convert them back to integers ( int(i) for i in ...
).
def int_join(joiner, arr):
return joiner.join(map(lambda i: str(i), arr))
def subDiff(ints):
element_sum = sum(ints)
digits = [ int(i) for i in list(int_join("", ints)) ]
digit_sum = sum(digits)
abs_diff = abs(element_sum - digit_sum)
int_list = int_join(" + ", ints)
digit_list = int_join(" + ", digits)
return (
abs_diff,
f"Element sum: {int_list} => {element_sum}\n" +
f"Digit sum: {digit_list} => {digit_sum}\n" +
f"Absolute difference: | {element_sum} - {digit_sum} | " +
f"=> {abs_diff}"
)
View the entire Python script for this task on GitHub.
Elixir
Like in Python, in Elixir the most involved part was getting the list of characters back into a list of integers. I’m using String.codepoints/1
because we’re dealing with numbers and we don’t have to worry about accents on the characters. Probably a better choice would have been String.graphemes/1
because it handles multi-character Unicode characters, and accented characters would come out as a single character, not as the base character and the accent.
But finally, getting the list of characters back to integers was a perfect task for Enum.map/2
, a function I rarely use, because I don’t usually need to apply a function to each element in a list and then get back the modified list; usually, I’m using Enum.map_reduce/3
because I’m more interested in the side effect of looping over the list than producing a modified list.
def int_join(ints, joiner), do: Enum.join(ints, joiner)
def subDiff(ints) do
element_sum = ints |> Enum.sum
digits = ints
|> Enum.join
|> String.codepoints
|> Enum.map(fn i -> String.to_integer(i) end)
digit_sum = digits |> Enum.sum
abs_diff = abs(element_sum - digit_sum)
int_list = int_join(ints, " + ")
digit_list = int_join(digits, " + ")
{
abs_diff,
"Element sum: #{int_list} => #{element_sum}\n" <>
"Digit sum: #{digit_list} => #{digit_sum}\n" <>
"Absolute difference: | #{element_sum} - #{digit_sum} |" <>
" => #{abs_diff}"
}
end
View the entire Elixir script for this task on GitHub.
Here’s all my solutions in GItHub: https://github.com/packy/perlweeklychallenge-club/tree/master/challenge-320/packy-anderson