Perl Weekly Challenge: Two Elements, Multiplied by Digit Sum

No music this week, only solutions to Perl Weekly Challenge 261!

Task 1: Element Digit Sum

You are given an array of integers, @ints.

Write a script to evaluate the absolute difference between element and digit sum of the given array.

Example 1

Input: @ints = (1,2,3,45)
Output: 36

Element Sum: 1 + 2 + 3 + 45 = 51
Digit Sum: 1 + 2 + 3 + 4 + 5 = 15
Absolute Difference: | 51 - 15 | = 36

Example 2

Input: @ints = (1,12,3)
Output: 9

Element Sum: 1 + 12 + 3 = 16
Digit Sum: 1 + 1 + 2 + 3 = 7
Absolute Difference: | 16 - 7 | = 9

Example 3

Input: @ints = (1,2,3,4)
Output: 0

Element Sum: 1 + 2 + 3 + 4 = 10
Digit Sum: 1 + 2 + 3 + 4 = 10
Absolute Difference: | 10 - 10 | = 0

Example 4

Input: @ints = (236, 416, 336, 350)
Output: 1296

Approach

To me, this seems like an exercise in treating a list of numbers like integers in one case (element sum) and as a string of characters in another (digit sum).

Raku

Ok, I’ve been accused of writing my Raku like Perl, so I need to really lean into thinking in Raku, not Perl. Our solution function should accept a list, and we should probably use Raku’s Reduction Metaoperator to create our sums and our string of characters, and abs is a method on the Numeric role that numeric objects have.

sub elementDigitSum(@ints) {
  # [+] sums all the elements of @ints
  my $elementSum = [+] @ints;

  my $explain = 'Element Sum: '
              ~ @ints.join(' + ')
              ~ ' = ' ~ $elementSum;

  # use [~] to concatenate all the integers together
  # into a single string, then use split() to get the
  # individual digits
  my @digits   = ([~] @ints).split('', :skip-empty);
  # [+] sums all the elements of @digits
  my $digitSum = [+] @digits;

  $explain ~= "\n" ~ 'Digit Sum: '
              ~ @digits.join(' + ')
              ~ ' = ' ~ $digitSum;

  my $abs = ($elementSum - $digitSum).abs;

  $explain ~= "\nAbsolute Difference: "
           ~ "| $elementSum - $digitSum | = $abs";

  return ($abs, $explain);
}

View the entire Raku script for this task on GitHub.

$ raku/ch-1.raku
Example 1:
Input: @ints = (1, 2, 3, 45)
Output: 36

Element Sum: 1 + 2 + 3 + 45 = 51
Digit Sum: 1 + 2 + 3 + 4 + 5 = 15
Absolute Difference: | 51 - 15 | = 36

Example 2:
Input: @ints = (1, 12, 3)
Output: 9

Element Sum: 1 + 12 + 3 = 16
Digit Sum: 1 + 1 + 2 + 3 = 7
Absolute Difference: | 16 - 7 | = 9

Example 3:
Input: @ints = (1, 2, 3, 4)
Output: 0

Element Sum: 1 + 2 + 3 + 4 = 10
Digit Sum: 1 + 2 + 3 + 4 = 10
Absolute Difference: | 10 - 10 | = 0

Example 4:
Input: @ints = (236, 416, 336, 350)
Output: 1296

Element Sum: 236 + 416 + 336 + 350 = 1338
Digit Sum: 2 + 3 + 6 + 4 + 1 + 6 + 3 + 3 + 6 + 3 + 5 + 0 = 42
Absolute Difference: | 1338 - 42 | = 1296

Perl

Sigh. Even though I tried to lean into making the Raku version use more Raku features, it turns out that [+] is just a built-in version of List::Util’s sum, and [~] is just a neater version of join('', @array).

use List::Util qw( sum );

sub elementDigitSum(@ints) {
  my $elementSum = sum @ints;

  my $explain = 'Element Sum: '
              . join(' + ',  @ints)
              . ' = ' . $elementSum;

  # use join() to concatenate all the integers together
  # into a single string, then use split() to get the
  # individual digits
  my @digits   = split //, join('', @ints);
  my $digitSum = sum @digits;

  $explain .= "\nDigit Sum: "
           . join(' + ',  @digits)
           . ' = ' . $digitSum;

  my $abs = abs($elementSum - $digitSum);

  $explain .= "\nAbsolute Difference: "
           . "| $elementSum - $digitSum | = $abs";

  return ($abs, $explain);
}

View the entire Perl script for this task on GitHub.

Python

I will, however, gladly cop to my Python looking like Perl, because it does. That’s because I don’t believe there’s a lot of difference between the languages. Though, when I’m writing my Python, I start with the Raku version because both Raku and Python have the same “everything is an object” edict at their heart.

def plus_join(arr):
    return ' + '.join(map(lambda i: str(i), arr))

def elementDigitSum(ints):
    elementSum = sum(ints)

    explain = f'Element Sum: {plus_join(ints)} = {elementSum}'

    # concatenate all the integers together into a single
    # string
    digitStr = ''.join([ str(i) for i in ints ])
    # loop over the individual digits
    digits = [ int(d) for d in digitStr ]
    digitSum = sum(digits)

    explain += "\n"
    explain += f'Digit Sum: {plus_join(digits)} = {digitSum}'

    absVal = abs(elementSum - digitSum)

    explain += "\n"
    explain += 'Absolute Difference: '
    explain += f'| {elementSum} - {digitSum} | = {absVal}'

    return (absVal, explain)

View the entire Python script for this task on GitHub.


Task 2: Multiply by Two

You are given an array of integers, @ints and an integer $start..

Write a script to do the followings:

a) Look for $start in the array @ints, if found multiply the number by 2
b) If not found stop the process otherwise repeat

In the end return the final value.

Example 1

Input: @ints = (5,3,6,1,12) and $start = 3
Output: 24

Step 1: 3 is in the array so 3 x 2 = 6
Step 2: 6 is in the array so 6 x 2 = 12
Step 3: 12 is in the array so 12 x 2 = 24

24 is not found in the array so return 24.

Example 2

Input: @ints = (1,2,4,3) and $start = 1
Output: 8

Step 1: 1 is in the array so 1 x 2 = 2
Step 2: 2 is in the array so 2 x 2 = 4
Step 3: 4 is in the array so 4 x 2 = 8

8 is not found in the array so return 8.

Example 3

Input: @ints = (5,6,7) and $start = 2
Output: 2

2 is not found in the array so return 2.

Approach

Well, this is a fairly straightforward loop, the interesting part is checking to see if $start is in the array. The boring way would be to loop over the elements of the array, but each of the languages I’m using have more interesting ways to

Raku

In Raku, we have a data type called a Set, and it has an infix (elem) operator which can be written with the unicode character ∈.

sub multiplyByTwo(@ints, $s) {
  my $start = $s; # so we can modify the value
  my $ints = Set(@ints);
  my @explain;
  my $step = 0;

  while ($start ∈ $ints) {
    $step++;
    my $old = $start;
    $start *= 2;
    @explain.push(
      "Step $step: $old is in the array so $old x 2 = $start"
    );
  }
  @explain.push(
    "$start is not in the array so return $start."
  );
  return ($start, @explain.join("\n"));
}

View the entire Raku script for this task on GitHub.

$ raku/ch-2.raku
Example 1:
Input: @ints = (5, 3, 6, 1, 12) and $start = 3
Output: 24

Step 1: 3 is in the array so 3 x 2 = 6
Step 2: 6 is in the array so 6 x 2 = 12
Step 3: 12 is in the array so 12 x 2 = 24
24 is not in the array so return 24.

Example 2:
Input: @ints = (1, 2, 4, 3) and $start = 1
Output: 8

Step 1: 1 is in the array so 1 x 2 = 2
Step 2: 2 is in the array so 2 x 2 = 4
Step 3: 4 is in the array so 4 x 2 = 8
8 is not in the array so return 8.

Example 3:
Input: @ints = (5, 6, 7) and $start = 2
Output: 2

2 is not in the array so return 2.

Perl

Perl, on the other hand, doesn’t have a Set data type, but we can easily do the same thing with a hash!

sub multiplyByTwo($start, @ints) {
  my %ints = map { $_ => 1 } @ints;
  my @explain;
  my $step = 0;

  while ( exists $ints{$start} ) {
    $step++;
    my $old = $start;
    $start *= 2;
    push @explain,
      "Step $step: $old is in the array so $old x 2 = $start";
  }
  push @explain,
    "$start is not in the array so return $start.";
  return ($start, join("\n", @explain));
}

View the entire Perl script for this task on GitHub.

Python

Python, however, does have a set datatype. In this case, because we don’t need to change the set after we create it, I’m going to use a frozenset.

def multiplyByTwo(ints, start):
    intSet = frozenset(ints)
    explain = []
    step = 0

    while start in intSet:
        step += 1
        old = start
        start *= 2
        explain.append(
          f"Step {step}: {old} is in the array " +
          f"so {old} x 2 = {start}"
        )
    explain.append(
      f"{start} is not in the array so return {start}."
    )
    return (start, "\n".join(explain))

View the entire Python script for this task on GitHub.


Here’s all my solutions in GItHub: https://github.com/packy/perlweeklychallenge-club/tree/master/challenge-261/packy-anderson