This is my response to the Perl Weekly Challenge #078.
@A
containing distinct integers.
@A
. Print
(0)
if none found.
Input: @A = (9, 10, 7, 5, 6, 1)
Output: (10, 7, 6, 1)
Input: @A = (3, 4, 5)
Output: (5)
#! /usr/bin/env raku
unit sub MAIN (*@A where @A.elems && all(@A) ~~ Int); # [1]
my @B;
while (@A) # [2a]
{
my $a = @A.shift; # [2]
@B.push: $a if $a > all(@A); # [3]
}
say '(' ~ @B.join(', ') ~ ')'; # [4]
[1] We cannot slap a type constraint on a slurpy argument, so we use
a where
clause instead. We insist on at least one element, as slurpies
accept none as well, and they must all be integers using an all
junction.
[2] Remove one element at a time from the front of the array, until we are done.
[3] Add the removed element to the result, if it is higher than what is left in the array
[4] Print it.
See
docs.raku.org/routine/all for
more information about all
, and
docs.raku.org/type/Junction for
more information about Junctions.
Running it with the values given in the challenge:
$ ./leader-element 9 10 7 5 6 1
(10, 7, 6, 1)
$ ./leader-element 3 4 5
(5)
The last element in @A
is always shown, so we get this:
./leader-element 91
(91)
./leader-element 0
(0)
This shows two things. First that the «Print
(0)
if none found» part of the description does not make sense. Except if we
allow an empty array. Which I have chosen not to. Second that we can get that
(0)
from a perfectly legal input array.
#! /usr/bin/env perl
use Perl6::Junction 'all';
use feature 'say';
my @A = @ARGV;
die "Please specify at least one element" unless @A;
die "Integers only" unless all(@A) == qr/^\d+$/;
my @B;
while (@A)
{
my $a = shift @A;
push(@B, $a) if $a > all(@A);
}
say '(' . join(', ', @B) . ')';
It works exactly like the Raku version:
$ ./leader-element-perl 9 10 7 5 6 1
(10, 7, 6, 1)
$ ./leader-element-perl 3 4 5
(5)
$ ./leader-element-perl 91
(91)
$ ./leader-element-perl 0
(0)
@A
containing positive numbers and @B
containing one or
more indices from the array @A
.
@A
so that the number at the first index of @B
becomes the first element in the array. Similary, left rotate @A
again so that the number
at the second index of @B
becomes the first element in the array.
Input:
@A = (10 20 30 40 50)
@B = (3 4)
Explanation
) We left rotate the 3rd index element (40) in the @A to make it 0th
index member in the array.
[40 50 10 20 30]
b) We left rotate the 4th index element (50) in the @A to make it 0th
index member in the array.
[50 10 20 30 40]
Output:
[40 50 10 20 30]
[50 10 20 30 40]
Input:
@A = (7 4 2 6 3)
@B = (1 3 4)
Explanation
a) We left rotate the 1st index element (4) in the @A to make it 0th
index member in the array.
[4 2 6 3 7]
b) We left rotate the 3rd index element (6) in the @A to make it 0th
index member in the array.
[6 3 7 4 2]
c) We left rotate the 4th index element (3) in the @A to make it 0th
index member in the array.
[3 7 4 2 6]
Output:
[4 2 6 3 7]
[6 3 7 4 2]
[3 7 4 2 6]
I'll start with a very short program, as it is funny to do so after the rather elaborate explanation:
File: left-rotation-1
#! /usr/bin/env raku
my @A = (10, 20, 30, 40, 50);
my @B = (3, 4);
@B.map({ @A.rotate($_).say }); # [1]
[1] rotate
gives a rotated copy, leaving the original array
unchanged. So the next iteration (so to speak) of the map
loop starts with the
same @A
. The values in the @B
array are the number of steps to
rotate, so we can use them directly, in the map
.
See
docs.raku.org/routine/rotate for
more information about rotate
Running it:
$ ./left-rotation-1
(40 50 10 20 30)
(50 10 20 30 40)
And a second program, for the second example:
File: left-rotation-2
#! /usr/bin/env raku
my @A = (7, 4, 2, 6, 3);
my @B = (1, 3, 4);
@B.map({ @A.rotate($_).say });
Running that one as well:
$ ./left-rotation-2
(4 2 6 3 7)
(6 3 7 4 2)
(3 7 4 2 6)
The challenge specified the values in the arrays (the input) without separating commas. That is probably an omission, as they are there in the first challenge. The output is without commas, but with the default parens instead of square brackets. I'll get back to that.
A more flexible approach, where the user can specify the two arrays, is certainly better:
File: left-rotation
#! /usr/bin/env raku
unit sub MAIN (Str $A, Str $B); # [1]
my @A = $A.words;
my @B = $B.words;
die '@A must be positive numbers only' unless all(@A) > 0; # [1]
die '@B must be legal indices only' unless 0 <= all(@B) <= @A.end; # [2]
@B.map({ say '[' ~ @A.rotate($_).join(' ') ~ ']' }); # [3]
[1] Two arrays on the command line does not work. But we can use quotes to group
them, and get the individual values with words
afterwards.
[2] Ensure that all the elements in @A
are positive. Any
non-numeric values will cause a runtime exception, which is ok.
[3] Ensure that all the elements in @B
are legal indices.
Non-integers will work (be truncated), which is ok-ish.
[4] The challenge wanted square brackets on the output, so here they are.
Running it:
$ ./left-rotation "10 20 30 40 50" "3 4"
[40 50 10 20 30]
[50 10 20 30 40]
$ ./left-rotation "7 4 2 6 3" "1 3 4"
[4 2 6 3 7]
[6 3 7 4 2]
[3 7 4 2 6]
$ ./left-rotation "7 4 2 6 3" "1 3.14 4"
[4 2 6 3 7]
[6 3 7 4 2]
[3 7 4 2 6]
$ ./left-rotation "7 4 2 6 A" "1 3 4"
Cannot convert string to number: base-10 number must begin with valid digits
or '.' in ...
$ ./left-rotation "10 20 30 40 50" "1 2 3 4 5"
@B must be legal indices only in ...
#! /usr/bin/env perl
use feature 'say';
use feature 'signatures';
use Perl6::Junction 'all';
no warnings qw(experimental::signatures);
my @A = split(" ", $ARGV[0]);
my @B = split(" ", $ARGV[1]);
die 'Specify @A' unless @A;
die 'Specify @B' unless @B;
die '@A must be positive numbers only' unless all(@A) > 0;
die '@B must be legal indices only' unless 0 <= all(@B) && all(@B) <= $#A;
map { say '[' . join(' ', do_rotate($_, @A)) . ']' } @B;
sub do_rotate ($length, @array) # [1]
{
push(@array, shift @array) for 1..$length;
return @array;
}
[1] I needed a rotate function that leaves the original array unchanged, so had to write this procedure.
It gives the same output as the Raku version:
$ ./left-rotation-perl "10 20 30 40 50" "3 4"
[40 50 10 20 30]
[50 10 20 30 40]
$ ./left-rotation-perl "7 4 2 6 3" "1 3 4"
[4 2 6 3 7]
[6 3 7 4 2]
[3 7 4 2 6]
$ ./left-rotation-perl "7 4 2 6 3" "1 3.14 4"
[4 2 6 3 7]
[6 3 7 4 2]
[3 7 4 2 6]
$ ./left-rotation-perl "10 20 30 40 50" "1 2 3 4 5"
@B must be legal indices only at ./left-rotation-perl line 16.
$ ./left-rotation-perl "7 4 2 6 A" "1 3 4"
@A must be positive numbers only at ...
And that's it.