Scan()和Reduce()是两个有代表性的迭代函数,以Scan()函数为例 ,它的语法格式如下:
SCAN ([initial_value], array, lambda(accumulator, value))
[initial_value] 设置累加器的起始值。
array 要扫描的数组。
lambda 接受调用而用于扫描数组的 LAMBDA。 LAMBDA 接受两个参数:
accumulator 该值累加后作为最终结果返回。
value 应用于数组中每个元素的计算。
首先它设置累加器的起始值,然后指定要扫描的数组,这个数组可以是一个单元格的区域,也可以是常量数组(如sequence()函数,row()函数创建的数组),然后按照顺序遍历这个数组,结合LAMBDA函数进行运算,遍历一次,运算完的结果就赋值给累加器,这个累加器的新值就作为下一次遍历运算时新的起始值,如此这样,不断循环,直到遍历完整个数组。
何谓遍历?简单说相当于VBA中的"For Each"循环语句,PYTHON语言也有类似的程序结构,这对于函数来说是一个大的突破,有了“编程”的能力。何谓迭代?有点类似于滚雪球,给累加器赋一个起始值 ,遍历一次后的结果成为为累加器的新值 ,它作为下一轮运算的起始值 ,运算完之后的结果又作为累加器的新值 ,不断循环,直到遍历完数组中的各个元素。
SCAN函数返回包含所有中间结果的数组,而REDUCE函数仅返回最终单一结果。所以这两个函数是非常类似的,REDUCE函数更常用一些。这两个函数的参加可以嵌套其他函数,它们的运算结果也可以作为其他函数的参数,所以,综合运用起来实在是千变万化,不过刚开始接触迭代函数,理解起来有点困难,所以把最基本的运算过程理解了,也就可以灵活运用了。
先来个简单的,从1加到10,结果是55,来看这两个函数运算的特点:
=SCAN(0,ROW(1:10),LAMBDA(x,y,SUM(x,y)))
=REDUCE(0,ROW(1:10),LAMBDA(x,y,SUM(x,y)))
上面公式中,累加器的起始值为0,要扫描的数组为ROW(1:10), 0作为累加器的起始值赋值给LAMBADA函数中的变量x, ROW(1:10)传递给变量y,ROW(1:10) 实际上就是{1,2,3,4,5,6,7,8,10},这10个值 从1开始依次进行运算,具体的运算方式就是sum(x,y).
当x=0,y=1时,sum(x,y)=1,第一次运算结束,x作为累加器的新值变成了1。遍历下一个值时,x=1, y=2, sum(x,y)=3,此时x新值为3...不断循环,直到y=10, x=45, sum(x,y)=55,x的新值为55。REDUCE函数直接输出最终结果55,而SCAN函数则把每一步运算的x值记录下来。
SCAN函数会把每一步运算的结果都记录下来,REDUCE函数则只保留最后一次的运算结果,这是两个函数的根本不同之处。
再来一个例子来分步演示整个运算过程, 目的是先将单元格A2:A10中的值复制到单元格D2:D10, 不要有合并单元格,重复的内容也要填充进去,如D2:D4重复显示为“西瓜”。
=SCAN("",A2:A10,LAMBDA(x,y,IF(y<>"",y,x)))
以上公式中,累加器的起始值为"",即空值, 要扫描的组为单元格区域A2:A10, 前面两值分别传递给变量x,y (变量名称可以随便定义),运算方式为:IF(y<>"",y,x), 为方便理解 ,我把每一步运算的过程都记录下来:
根据分析,我们可以找到下面一些规律(请注意上面标红色的E,F,G,H所指代的内容):
1. G=H ,就是说每次运算结束后,运算的结果将作为累加器的新值。
2. H(n)=E(n+1), n表示第几次扫描,如n=1时,第一次扫描后的H
值,将作为下一轮(n+1)累加器的起始值。
3. SCAN函数输出所有的H值, REDUCE函数只输出最后一个H值。
如果把公式改成REDUCE,最后的结果只是"香瓜"。
=REDUCE("",A2:A10,LAMBDA(x,y,IF(y<>"",y,x)))
理解了运算过程,就可以进行扩展应用,下面再分享三个具体的例子。
案例1:
=LET(a,A2:A10,b,C2:C10,c,SCAN("",a,LAMBDA(x,y,IF(y<>"",y,x))),
d,HSTACK(c,b),
GROUPBY(TAKE(d,,1),TAKE(d,,-1),SUM,0,0))
其实还是上面的例子,最终的结果是要统计每种水果总的数量。在LET函数中定义变量,变量c就是上面讲的,变量d就是把c,b进行横向拼接,最后进行GROUPBY运算。
案例2:
=DROP(REDUCE("",A1:A2,
LAMBDA(x,y,VSTACK(x,TEXTSPLIT(y,",")))),1)
目的是将每一行的文本分列,然后纵向合并。
案例3:
=SCAN(0,SEQUENCE(COUNTA($A$1:$A$30)/10,1,1,10),
LAMBDA(x,y,AVERAGE(INDIRECT("A"&y):INDIRECT("A"&(y+9)))))
目的是将单元格区域A1:A30中,第1至第10个数值求平均值,第11至20个数值求平均值,第21至30个值求平均值。
要遍历的数组是SEQUENCE(COUNTA($A$1:$A$30)/10,1,1,10),其实就是{1,11,21},后面INDIRECT函数所引用的是A1:A10, A11:A20, A21:A30, 分别对应于y=1,11,21, 并且区域当中终点的单元格的行数比起点的单元格行数多9(y+9)。在些基础上进行AVERAGE聚合运算。如果改为REDUCE函数,就只输出最后一个值25.5。

