映射一组顺序列表元素的简单方法

Simple way to do mapping of group of sequential list elements

法线映射将函数应用于列表元素并生成结果列表的元素。例如,如果列表是 (1, 2, 3,) 并映射平方函数,您将得到一个新列表 (1, 4, 9,).

有没有办法映射列表的一组顺序元素?例如,如果列表是 <8 2 7 2 6 9 4 9 6 1> 并且我想计算列表中每 2 个元素的总和以使 <10 9 9 8 15 13 13 15 7>?

我当然可以写一个遍历列表的例程。但我正在寻找一种更简单的方法,例如缩减运算符或 gather/take。

执行此操作的函数式编程方法是引用列表两次,将一个版本偏移一个元素,然后将它们压缩在一起。这为您提供了后续元素的所有元组。

my @l := <a b c d e f>;
say @l Z @l[1..*]; # output: ((a b) (b c) (c d) (d e) (e f))

如果您不想创建一个临时变量来保存列表,请使用 given(如果您需要 return 一个值的语句,请使用 do given):

given <8 2 7 2 6 9 4 9 6 1> {
  say ($_ Z $_[1..*]).map: -> [$a, $b] { $a+$b };
}

如果您使用的函数带有两个参数,您可能希望在压缩后展平列表并让 map 一次带两个元素:

given <8 2 7 2 6 9 4 9 6 1> {
  say ($_ Z $_[1..*]).flat.map(&infix:<+>);
}

您可以使用 .rotor 方法将列表划分为重叠的子列表:

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))

2 => -1 是一个 Pair 参数,它向方法表明它应该通过在每一步 "two forward, one back" 生成子列表。

然后您可以简单地使用 .map 将您的操作(例如求和)应用于每个子列表:

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)