Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I slice a shaped array in Perl 6?

I can make a shaped (fixed-size) array:

my @array[3;3] = (
    < 1 2 3 >,
    < 4 5 6 >,
    < 7 8 9 >
    );

say @array;       # [[1 2 3] [4 5 6] [7 8 9]]
say @array[1;1];  # 5

How can I slice this to get any particular column or diagonal that I want (rows are easy)?

How do I turn a list of the indices in each dimension into the right thing to put in the square braces?

And, surely there's some fancy syntax that would keep me from doing something complicated:

my @diagonal = gather {
    my @ends = @array.shape.map: { (0 ..^ $^a).List };
    for [Z] @ends {
        take @array[ $_ ] # how do I make that $_[0];$_[1];...
        };
    }
like image 513
brian d foy Avatar asked May 16 '18 01:05

brian d foy


2 Answers

How can I slice this to get any particular column or diagonal that I want?

As far as I know, you can't currently use slice syntax with shaped arrays (notwithstanding your "(rows are easy)" comment which confuses me per my comment on your post).

The obvious solution is to drop the shape and use slice syntax:

my @array = ( < 1 2 3 >, < 4 5 6 >, < 7 8 9 > );

say @array[1];         # 4 5 6 (second row)
say @array[1;*];       # same
say @array[*;1];       # 2 5 8 (second column)

If you wanted to retain the bounds-checking safety of using a shaped array (and/or the C array compatibility of a shaped native array if I'm right that's a thing) then you'd presumably have to keep two copies of the array around, using one to retain the desired aspect of shaped arrays, the other to slice.

How do I turn a list of the indices in each dimension into the right thing to put in the square braces?

Each dimensional slice before the final leaf one must be separated from the next by a ;.

I'm not yet clear on whether that's because the ; is a statement separator (within the subscript) or a list-of-list indicator, nor how to programmatically turn a list of indices into that form. (Investigation continues.)

And, surely there's some fancy syntax that would keep me from doing something complicated [for a diagonal slice]:

say @array[*;{$++}];   # 1 5 9 (diagonal)

The first ; separated field in the [...] array subscript corresponds to the first dimension in the array, i.e. the rows in the array.

Specifying * means you want to include all rows rather than specify the specific row(s).

The last field corresponds to the leaves of the subscript, the actual elements to be accessed.

I first tried just $++ rather than {$++} but that gave me column zero for all elements presumably because the language/roast and/or Rakudo only evaluates a scalar index value once per call of the [...] subscript operator.

Then I reasoned that if an index is Callable it'll be called and it might be called once per row. And that worked.

I think that corresponds to this code in Rakudo.

At first glance this appears to mean you can't use a Callable to calculate a leaf slice and I note that the roast'd slicing for "calculated indices" doesn't include use of a Callable. Perhaps I'm just not looking at it right.

like image 150
raiph Avatar answered Oct 21 '22 16:10

raiph


You probably have seen that that returns a not yet implemented error (which was inserted to solve this bug;

Partially dimensioned views of shaped arrays not yet implemented. Sorry.

In this case, it might be better to just unshape the array and use a more traditional approach:

use v6;

my @array = (
    < 1 2 3 >,
    < 4 5 6 >,
    < 7 8 9 >
    );

my @diagonal = gather {
    my @ends = ((0,0),(1,1),(2,2));
    for @ends -> @indices {
        take @array[ @indices[0] ][@indices[1]];
    };
}
say @diagonal;

By looking at the synopsis on the subject, I would say that approach is not really specified. So when all is said and done, you will probably have to use either EVAL or macros (when they are eventually implemented, of course... )

like image 45
jjmerelo Avatar answered Oct 21 '22 15:10

jjmerelo