lisprogtor
lisprogtor

Reputation: 5749

raku What is a better way to do rotor from the end?

If data at the end of a list are more important, and I want :partial list to be left at the beginning of the list while preserving the initial order, I do this:

> my @a = (0,1,2,3,4,5,6,7,8,9);
[0 1 2 3 4 5 6 7 8 9]
> say @a.rotor(4, :partial)
((0 1 2 3) (4 5 6 7) (8 9)) # not what I want; important data at end gets cut off;
> say @a.reverse.rotor(4, :partial).reverse.map({$_.reverse});
((0 1) (2 3 4 5) (6 7 8 9)) # this is what I want

Is there a way to avoid the 3 "reverse" operations? Is it possible to add a :fromEnd adverb?

Upvotes: 6

Views: 253

Answers (4)

Brad Gilbert
Brad Gilbert

Reputation: 34120

You could calculate the number of elements left in the :partial list.

@a.elems % 4

Then combine it with a repeating sequence of the size you wanted.

4 xx *

If you tried to combine them simply it won't work

@a.elems % 4, 4 xx *

That is because the above is a list of two elements. The first is the result of the modulus operation, and the second is an infinite sequence.

You can just use flat to make them a single sequence.

flat @a.elems % 4, 4 xx *

Here is the resulting code.

@a.rotor( flat @a.elems % 4, 4 xx * )

Upvotes: 1

wamba
wamba

Reputation: 4465

You can use @a.rotor(2,4,4) or a little more universal @a.rotor(@a % 4,slip 4 xx *). Of course, you could define function

multi batch ( $_, $n, :from-end($)! ) {
    .rotor: .elems % $n, slip $n xx *
}
say batch  ^10,  4,:from-end; #or even
say (^10).&batch(4):from-end;

Upvotes: 6

chenyf
chenyf

Reputation: 5058

my @a = (0,1,2,3,4,5,6,7,8,9);
my @b = @a.rotor(4, :partial)».elems.reverse;
say @a.rotor(|@b);

Upvotes: 6

Elizabeth Mattijsen
Elizabeth Mattijsen

Reputation: 26924

Taking the last question first: Yes, it is possible, but it would probably be called :end for consistency, and no I don't think it is likely to be added. But then I could be wrong :-)

I'm not sure how golfed your example is, but if you're really working from an array, why not stuff it first with values to make it evenly divisible by 4, and then remove them afterwards again:

my @a = ^10;
@a.splice(0,0, Any xx @a % 4);
say @a.batch(4);  # (((Any) (Any) 0 1) (2 3 4 5) (6 7 8 9))

Note I've used the shorter ^10 (which is a range from 0 to 10, excluding the endpoint). And I've used the simpler batch method, as we are sure there won't be any partial lists. In any case, no reverse is needed, just checking values afterwards.

Mind you, the reverse method on an array, is relatively cheap, as it does not actually move any values.

Upvotes: 3

Related Questions