Perl Weekly Challenge: Week 238

Challenge 1:

Running Sum

You are given an array of integers.

Write a script to return the running sum of the given array. The running sum can be calculated as sum[i] = num[0] + num[1] + …. + num[i].

Example 1
Input: @int = (1, 2, 3, 4, 5)
Output: (1, 3, 6, 10, 15)
Example 2
Input: @int = (1, 1, 1, 1, 1)
Output: (1, 2, 3, 4, 5)
Example 3
Input: @int = (0, -1, 1, 2)
Output: (0, -1, 0, 2)

Hooray we can do a one-liner again this week!

$t stores the running total. It is initially 0. .map() is used to iterate through the command line arguments adding each to the current value of $t. And that's it. The result is a transformed array where each element is the total of itself and all the elements preceding it. The rest of the code is just for outputting the results in a pretty way.

my $t=0;say q{(},@*ARGS.map({$t+=$_}).join(q{, }),q{)}

(Full code on Github.)

The Perl version is even shorter because, unlike in Raku, we don't need to initialize $t.

say q{(},(join q{, },map{$t+=$_}@ARGV),q{)}

(Full code on Github.)

Challenge 2:

Persistence Sort

You are given an array of positive integers.

Write a script to sort the given array in increasing order with respect to the count of steps required to obtain a single-digit number by multiplying its digits recursively for each array element. If any two numbers have the same count of steps, then print the smaller number first.

Example 1
Input: @int = (15, 99, 1, 34)
Output: (1, 15, 34, 99)

15 => 1 x 5 => 5 (1 step)
99 => 9 x 9 => 81 => 8 x 1 => 8 (2 steps)
1  => 0 step
34 => 3 x 4 => 12 => 1 x 2 => 2 (2 steps)
Example 2
Input: @int = (50, 25, 33, 22)
Output: (22, 33, 50, 25)

50 => 5 x 0 => 0 (1 step)
25 => 2 x 5 => 10 => 1 x 0 => 0 (2 steps)
33 => 3 x 3 => 6 (1 step)
22 => 2 x 2 => 4 (1 step)

To solve this problem in Raku we start by creating a hash that will map the integers provided to the number of steps it takes to reduce them to single-digit numbers.

    my %steps;

Then for each integer...

    for @ints -> $int {

...first we have to copy it into another variable $n because $int is immutable.

        my $n = $int;

We use a variable $s to count the number of steps taken. It is initially set to 0.

        my $s = 0;

Then while $n has more than one digit...

        while $n.chars > 1 {

...we split it into individual digits with .comb() and multiply all these digits together with [*]. The result becomes the new value of $n.

            $n = [*] ($n.comb);

And we increment the number of steps taken.

            $s++;
        }

Once we are down to a single digit, the original integer and the numbers of steps are added to the %steps hash. $n is thrown away; we won't need it.

        %steps{$int} = $s;
    }

Once we have the step count for all the integers, we can sort them into a new array in ascending numerical order by number of steps. When there is a tie, as the spec suggests, we place the smaller integer first.

    my @sorted = %steps.keys.sort({ %steps{$^a} <=> %steps{$^b} || $^a > $^b });

And then we nicely format the results and print them.

    say q{(}, @sorted.join(q{, }), q{)};

(Full code on Github.)

The Perl version only has one big difference.

my %steps;

for my $int (@ints) {
    my $n = $int;
    my $s = 0;
    while (length $n > 1) {

Because we don't have the handy [*] operator, we have to split $n into digits and loop through them multiplying them as we go.

        my $t = 1;
        my @digits = split //, $n;
        for my $digit (@digits) {
            $t *= $digit;
        }

The rest is the same.

        $n = $t;
        $s++;
    }
    $steps{$int} = $s;
}

my @sorted = sort { $steps{$a} <=> $steps{$b} || $a > $b } keys %steps;

say q{(}, (join q{, }, @sorted), q{)};

(Full code on Github.)