Could someone explain why this accesses the last element in Perl 6
@array[*-1]
and why we need the asterisk *
?
Isn't it more logical to do something like this:
@array[-1]
The user documentation explains that *-1
is just a code object, which could also be written as
-> $n { $n - 1 }
When passed to [ ]
, it will be invoked with the array size as argument to compute the index.
So instead of just being able to start counting backwards from the end of the array, you could use it to eg count forwards from its center via
@array[* div 2] #=> middlemost element
@array[* div 2 + 1] #=> next element after the middlemost one
According to the design documents, the reason for outlawing negative indices (which could have been accepted even with the above generalization in place) is this:
The Perl 6 semantics avoids indexing discontinuities (a source of subtle runtime errors), and provides ordinal access in both directions at both ends of the array.
If you don't like the whatever-star, you can also do:
my $last-elem = @array.tail;
or even
my ($second-last, $last) = @array.tail(2);
Edit: Of course, there's also a head
method:
my ($first, $second) = @array.head(2);
The other two answers are excellent. My only reason for answering was to add a little more explanation about the Whatever Star *
array indexing syntax.
The equivalent of Perl 6's @array[*-1]
syntax in Perl 5 would be $array[ scalar @array - 1]
. In Perl 5, in scalar context an array returns the number of items it contains, so scalar @array
gives you the length of the array. Subtracting one from this gives you the last index of the array.
Since in Perl 6 indices can be restricted to never be negative, if they are negative then they are definitely out of range. But in Perl 5, a negative index may or may not be "out of range". If it is out of range, then it only gives you an undefined value which isn't easy to distinguish from simply having an undefined value in an element.
For example, the Perl 5 code:
use v5.10;
use strict;
use warnings;
my @array = ('a', undef, 'c');
say $array[-1]; # 'c'
say $array[-2]; # undefined
say $array[-3]; # 'a'
say $array[-4]; # out of range
say "======= FINISHED =======";
results in two nearly identical warnings, but still finishes running:
c
Use of uninitialized value $array[-2] in say at array.pl line 7.
a
Use of uninitialized value in say at array.pl line 9.
======= FINISHED =======
But the Perl 6 code
use v6;
my @array = 'a', Any, 'c';
put @array[*-1]; # evaluated as @array[2] or 'c'
put @array[*-2]; # evaluated as @array[1] or Any (i.e. undefined)
put @array[*-3]; # evaluated as @array[0] or 'a'
put @array[*-4]; # evaluated as @array[-1], which is a syntax error
put "======= FINISHED =======";
will likewise warn about the undefined value being used, but it fails upon the use of an index that comes out less than 0:
c
Use of uninitialized value @array of type Any in string context.
Methods .^name, .perl, .gist, or .say can be used to stringify it to something meaningful.
in block <unit> at array.p6 line 5
a
Effective index out of range. Is: -1, should be in 0..Inf
in block <unit> at array.p6 line 7
Actually thrown at:
in block <unit> at array.p6 line 7
Thus your Perl 6 code can more robust by not allowing negative indices, but you can still index from the end using the Whatever Star syntax.
If you just need the last few elements of an array, I'd recommend using the tail
method mentioned in mscha's answer. @array.tail(3)
is much more self-explanatory than @array[*-3 .. *-1]
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With