=LET(date,B3:B9,n,COUNTA(date),hs,LAMBDA(hs,idx,IF(idx>n,"",LET(curr,INDEX(date,idx),IF(curr>=0,VSTACK(curr,hs(hs,idx+1)),
此时到了函数的这个部分。
紧接着,就是函数的第二部分,当Curr<0的时候,也就是数据跑着跑着,遇到了一个负数,负数该怎么处理的问题?
负数的思路,其实前面也提到了,就是一个累加的过程,从当前位置开始累加,直到第一个正数,或者是直到行数达到最大值。
因此,我们可以自定义一个序列,seq,
let(seq,SEQUENCE(n-idx+1,1,idx,1),例如,当数据为-3的时候,7-3+1=5,也就是构造成从-3一直到最后一个数据,所需要的行数,列数为1,开始从Idx开始,每次增加1,也就是3,4,5,6,7。
再定义累加的结果为acc,scan(0,seq,lambda(a,i,a+index(date,i))),
也就是把原数据从第三行开始,不断地累加,并保留累加过程。
由此,可以得出一个数据组acc:-3,-1,-0.7,0.3,1.3(以第一组数据为例)
可以看到,我们要的就是0.3这个数字。
下面先拆分第一组数据的结果,即累加中能出现正数。
因此,就要定位到0.3所在位置。所以使用函数match定位
也就是可以在这一层级的Let中,定义一个名称
pos,IFERROR(MATCH(TRUE,acc>=0,0),0)
这时,函数已经写到了:
=LET(date,B3:B9,n,COUNTA(date),hs,LAMBDA(hs,idx,IF(idx>n,"",LET(curr,INDEX(date,idx),IF(curr>=0,VSTACK(curr,hs(hs,idx+1)),LET(seq,SEQUENCE(n-idx+1,1,idx,1),acc,SCAN(0,seq,LAMBDA(a,i,a+INDEX(date,i))),pos,IFERROR(MATCH(TRUE,acc>=0,0),0),
下面再利用sequence构造0的数组,再利用Index取到acc中Pos的位置。
因此就有了下面的函数:
VSTACK(SEQUENCE(pos-1,1,0,0),INDEX(acc,pos)
这里也就是取了3个0,以及一个0.3
效果如下:
但是,我们不要忘了,我们在前面的if中,即Curr<0,这个分类中还没有递归,
我们该怎么递归呢?
可以发现,只有当pos>0的时候,才具有递归的意义,也就是把剩下的值,再套进函数中去运算,因此,也就是
IF(pos>0,LET(rest,hs(hs,idx+pos),VSTACK(SEQUENCE(pos-1,1,0,0),INDEX(acc,pos),rest))
在这个情况下,如果rest都是正数,那么他就会带入前面的If(curr>=0运算,如果是负数,则会继续在这个分支运算
如果Pos=0怎么办那?Pos等于0,就说明,剩下的数据累加都是小于0的值,
这个处理就简单了:直接
VSTACK(SEQUENCE(COUNTA(acc)-1,1,0,0),TAKE(acc,-1))
但是如果只剩下一个数,而且是负数,累加只有一行数,SEQUENCE(pos-1,1,0,0)就会出错,因此只需要前面加一个iferror就可以了~
IFERROR(VSTACK(SEQUENCE(COUNTA(acc)-1,1,0,0),TAKE(acc,-1)),TAKE(acc,-1))
这个处理,也就是第二组数据和第三组数据的结果
这样,整个函数就写完了~
补上所有的括号,注意变量是idx,那么也就是从1开始不停累加即可~
,hs(hs,1))也就对应到了这个,
再回顾一下这道题目递归的逻辑~
从第一行开始判断,如果值是正数,则Idx+1,如果当前行的值是负数,则进入scan函数为主的累加替换过程。