Perl Weekly Challenge: Week 228

Challenge 1:

Unique Sum

You are given an array of integers.

Write a script to find out the sum of unique elements in the given array.

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

In the given array we have 2 unique elements (1, 3).
Example 2
Input: @int = (1, 1, 1, 1)
Output: 0

In the given array no unique element found.
Example 3
Input: @int = (2, 1, 3, 4)
Output: 10

In the given array every element is unique.

Hooray! we can do a Raku one-liner again. What the code below does is to use the .classify() method on the list of command line arguments. This produces a hash where the keys are values found in the list and the values (bit of overlapping terminology there) are the number of times that value occured. We .grep() through that to find the keys which only occurred once. Why not use the more appropriate-sounding .unique() method? Because that will include all values that occured but only count them once. So e.g. calling it on example 1 would give us 1, 2, 3 not 1, 3 which is what we want. We now have a list of pairs. We use .map({.key}) to get the first member of each pair. (I suspect there is a more succint way of doing this but I couldn't think of it.) .sum() totals the resulting list and .say() prints that total. list and finally

@*ARGS.classify({$_}).grep({.value==1}).map({.key}).sum.say

(Full code on Github.)

Perl needs some help as usual. The function classifyUnique() actually combines the .classify(), .grep() and .map() steps of the Raku version.

sub classifyUnique {
    my %count;

    for my $n (@_) {
        $count{$n}++;
    }
    return grep { $count{$_} == 1; } keys %count;
}

sum() emulates the Raku method of the same name.

sub sum {
    my $total = 0;

    for my $elem (@_) {
        $total += $elem;
    }

    return $total;
}

With these helpers, the core of the Perl version also becomes a one-liner.

say sum(classifyUnique(@ARGV));

(Full code on Github.)

Challenge 2:

Empty Array

You are given an array of integers in which all elements are unique.

Write a script to perform the following operations until the array is empty and return the total count of operations.

If the first element is the smallest then remove it otherwise move it to the end.
Example 1
Input: @int = (3, 4, 2)
Ouput: 5

Operation 1: move 3 to the end: (4, 2, 3)
Operation 2: move 4 to the end: (2, 3, 4)
Operation 3: remove element 2: (3, 4)
Operation 4: remove element 3: (4)
Operation 5: remove element 4: ()
Example 2
Input: @int = (1, 2, 3)
Ouput: 3

Operation 1: remove element 1: (2, 3)
Operation 2: remove element 2: (3)
Operation 3: remove element 3: ()

This one was very easy. Here is the Raku version.

We declare a variable to count the operations.

my $ops = 0;

While me have elements left...

while @ints.elems {

If the first element is equal to the smallest (found with .min().) ...

    if @ints[0] == @ints.min {

...It is removed from the list...

        @ints.shift;

...otherwise it is removed from the list and replaced at the end of the list.

    } else {
        @ints.push(@ints.shift);
    }

In either case an operation has been performed so the counter is incremented.

    $ops++;
}

When there are no elements left, the number of operations is printed out.

say $ops;

(Full code on Github.)

The Perl is mostly the same.

my $ops = 0;

while (scalar @ints) {

Except we don't have .min() so we have to provide our own min() function.

    if ($ints[0] == min(@ints)) {
        shift @ints;
    } else {
        push @ints, shift @ints;
    }
    $ops++;
}

say $ops;

min() looks like this. All it does is sort the input array in ascending numerical order and returns the first element which will be the smallest.

sub min {
    return ( sort { $a <=> $b } @_)[0];
}

(Full code on Github.)