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