Normal mapping applies a function to a list element and produces an element of the resultant list. E.g., if list is (1, 2, 3,)
and maps the square function, you get a new list (1, 4, 9,)
.
Is there a way to map a group of sequential elements of a list? For instance, if the list is <8 2 7 2 6 9 4 9 6 1>
and I want to calculate the sum of every 2 elements of the list to make <10 9 9 8 15 13 13 15 7>
?
I can certainly write a routine to traverse the list. But I am looking for an easier way, like the reduction operator or like the gather/take.
You can use the .rotor
method to partition a list into overlapping sublists:
say <8 2 7 2 6 9 4 9 6 1>.rotor(2 => -1);
# Output:
# ((8 2) (2 7) (7 2) (2 6) (6 9) (9 4) (4 9) (9 6) (6 1))
The 2 => -1
is a Pair
argument which signifies to the method that it should generate the sublists by going "two forward, one back" at each step.
Then you can simply use .map
to apply your operation (e.g. sum) to each sublist:
say <8 2 7 2 6 9 4 9 6 1>.rotor(2 => -1).map(*.sum);
# Output:
# (10 9 9 8 15 13 13 15 7)
The functional programming way to do this is to reference the list twice, offset one version by an element, and zip them together. This gives you all the tuples of subsequent elements.
my @l := <a b c d e f>;
say @l Z @l[1..*]; # output: ((a b) (b c) (c d) (d e) (e f))
If you don't want to create a temporary variable to hold the list, use given
(and use do given
if you need the statement to return a value):
given <8 2 7 2 6 9 4 9 6 1> {
say ($_ Z $_[1..*]).map: -> [$a, $b] { $a+$b };
}
If you're using a function that takes two parameters, you may want to flatten the list after zipping and let map
take two elements at a time:
given <8 2 7 2 6 9 4 9 6 1> {
say ($_ Z $_[1..*]).flat.map(&infix:<+>);
}
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