Perl Weekly Challenge 122: mangling numbers
One way to let me improve my knowledge about Raku (aka Perl 6) is to implement programs in it. Unluckily, I don’t have any production code to implement in Raku yet (sob!). So, why not try solving the Perl Weekly Challenge tasks?In the following, the assigned tasks for Challenge 122.
PWC 122 - Task 1
The first task was about computing the average of a stream of numbers. Since it is not clear to me if the word stream here means a continuos input from the user, I provided two differentMAIN
s: one that gets an array of numbers, and one that reads from STDIN
.
The application is as follows:
multi sub MAIN( *@N where { @N.elems == @N.grep( * ~~ Int ).elems }) {
my @average;
@average.push: (@N[ 0 .. @average.elems - 1 ].sum + $_) / ( @average.elems + 1 ) for @N;
@average.say;
}
multi sub MAIN() {
"Insert a number at time".say;
my @average;
my $sum = 0;
for lines() {
"Not a number".say && next if $_ !~~ / \d+ /;
$sum += $_;
@average.push: $sum / ( @average.elems + 1 );
"Average trend so far: { @average.say }";
}
}
The streaming
MAIN
(the second one) is a little boring: it accumulates the sum of provided numbers into a $sum
variable, and pushes the computed average into an @average
array. I don’t keep track of how many numbers have been provided, since @average
contains that information too. Also, I do test for good numbers before computing any value.
The first
MAIN
does pretty much the same, but the @average
array is populated within a for
loop that scans every input number, and the average value is made by summing the current value with the slice of previous values on the input @N
array.
PWC 122 - Task 2
The second task was about finding out all possible sequences of available baseball scores that provide a final score specified as input.I decided to let Raku do a lot of computations, producing all available permutations of numbers and throwing away those that do not provide the right final score. I could not come up with a simpler solution, from the point of view of code, than this, but clearly it is strongly de-optimized!
sub MAIN( Int $S ) {
my @available-points = 1, 2, 3;
my $begin = ( @available-points[ 0 ] x $S ).Int;
my $end = ( @available-points[ * - 1 ] x $S ).Int;
my @scores;
for ( $begin .. $end ) {
my @digits = $_.split( '', :skip-empty ).grep( * == any( @available-points ) );
next if ! @digits.grep: $_ for @available-points;
next if @digits.sum != $S;
my $score = @digits.grep( * == any( @available-points ) ).join;;
@scores.push: $score if ( ! @scores.grep( $score ) );
}
@scores.join( "\n" ).say;
}
The idea is that, assuming the final score
$S
as 4
, I got all values between 1111
and 3333
. After that I check if the number, split into its @digits
can give me a sum up to $S
, and in the case I place it into the @scores
array, after trimming out all zeros. If the number includes digits that are not within the @available-points
or if the sum of those digits is not the one I’m looki9ng for, I discard the number.
Trimming out the
0
from a number means that the @scores
array is going to include duplicates (e.g., 1002
and 1200
will both result in 12
), so at the end I apply unique
to get a single value out of all duplicates.