Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing the last element in Perl6

Tags:

raku

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] 
like image 945
smith Avatar asked Jan 28 '17 20:01

smith


3 Answers

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.

like image 149
Christoph Avatar answered Nov 01 '22 22:11

Christoph


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);
like image 12
mscha Avatar answered Nov 01 '22 21:11

mscha


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.

last word of advice

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].

like image 7
Christopher Bottoms Avatar answered Nov 01 '22 22:11

Christopher Bottoms