10

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.

Pat
  • 36,282
  • 18
  • 72
  • 87
lisprogtor
  • 5,677
  • 11
  • 17

2 Answers2

11

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)
smls
  • 5,738
  • 24
  • 29
6

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:<+>);
}
piojo
  • 6,351
  • 1
  • 26
  • 36
  • 1
    Thank you very much, piojo !! Your answer really helped !! Is there a reason you used @[$a,$b] instead of [$a,$b] or ($a,$b)? They all give the same results. Thanks again! – lisprogtor Dec 27 '17 at 09:27
  • @smls Thanks, I changed it. – piojo Dec 27 '17 at 09:41
  • @lisprogtor I didn't realize you could destructure without a variable name or sigil. Thanks for letting me know. I updated the example. – piojo Dec 27 '17 at 09:43
  • 3
    why not use `Z+` to perform the addition? – Christoph Dec 27 '17 at 14:30
  • 1
    @Christoph I don't think addition is the OP's real goal. It sounds like a placeholder to take the place of a more significant operation. – piojo Dec 27 '17 at 15:07