Perl Weekly Challenge: Happy Mother’s Day

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

Leave a Reply