上次我们介绍了使用Excel函数实现for循环(Excel函数循环之For循环 - Excel函数式编程),最后我们留了两个问题:
先看第一个问题。 原来的函数定义是这样的: /**For循环= ForLoop(data, from, to, step,init_value, func)*/ForLoop = LAMBDA( data, // 数据区域或数组 from, // 其实索引号 to, // 结束索引号 step, // 步长 init_value, // 循环初值 func, // 循环处理函数 LET( loopArr, SEQUENCE((to - from) / step + 1,,from, step), // 索引数组,比如,from = 1, to = 5, step = 2,就会生成一个{1;3;5}的索引数组 SCAN( //采用SCAN,返回一个数组 init_value, loopArr, // 对于索引数组进行循环 LAMBDA(acc, i, func(acc, i, data)) // 使用循环处理函数参数进行数据处理 ) )); 第13行使用了SCAN。这里可以使用REDUCE函数来代替。 我们的目标是在使用“For循环”时自行决定使用哪个函数,就需要讲这个选择作为这个For循环函数的参数。 所以我们增加一个参数: ForLoop1 = LAMBDA( scan_or_reduce, //scan or reduce data, // 数据区域或数组 from, // 其实索引号 to, // 结束索引号 step, // 步长 init_value, // 循环初值 func, // 循环处理函数 第一行就是新增加的参数,如果希望返回数组,调用时,这里就写scan,如果希望返回数值,这里就写reduce。 其余的参数没有变化。 函数体中只要将原来的SCAN换成这个参数(scan_or_reduce)即可, /**For循环= ForLoop1(scan_or_reduce, data, from, to, step,init_value, func)*/ForLoop1 = LAMBDA( scan_or_reduce, //scan or reduce data, // 数据区域或数组 from, // 其实索引号 to, // 结束索引号 step, // 步长 init_value, // 循环初值 func, // 循环处理函数 LET( loopArr, SEQUENCE((to - from) / step + 1,,from, step), // 索引数组,比如,from = 1, to = 5, step = 2,就会生成一个{1;3;5}的索引数组 scan_or_reduce( //采用SCAN,返回一个数组 init_value, loopArr, // 对于索引数组进行循环 LAMBDA(acc, i, func(acc, i, data)) // 使用循环处理函数参数进行数据处理 ) )); 第14行的就是用了这个参数。 调用时,在第一个参数中写SCAN, 返回的就是数组。 如果写REDUCE,返回的就是一个数值, 在这个自定义函数中, /**For循环= ForLoop1(scan_or_reduce, data, from, to, step,init_value, func)*/ForLoop1 = LAMBDA( scan_or_reduce, //scan or reduce data, // 数据区域或数组 from, // 其实索引号 to, // 结束索引号 step, // 步长 init_value, // 循环初值 func, // 循环处理函数 LET( loopArr, SEQUENCE((to - from) / step + 1,,from, step), // 索引数组,比如,from = 1, to = 5, step = 2,就会生成一个{1;3;5}的索引数组 scan_or_reduce( //采用SCAN,返回一个数组 init_value, loopArr, // 对于索引数组进行循环 LAMBDA(acc, i, func(acc, i, data)) // 使用循环处理函数参数进行数据处理 ) )); 是否可以使用BYROW代替这里的SCAN或者REDUCE呢? 从案例中我们引用这个函数的方式来看, =ForLoop1( REDUCE, A2:B5, 1, 4, 2, "", LAMBDA(acc,i,data, acc & INDEX(data, i, 1))) 第8行中我们使用了INDEX(data,i,1)来返回数据其余中的一行,然后交给第8行定义的lambda函数做处理。 这么说,完全可以用BYROW函数来代替,反正我们只是逐行处理。 这样的话,我们甚至都不用第行这个自定义函数,这里只需要写一个BYROW就可以了。 不过我们不建议这么做。 因为BYROW只能逐行处理,它没有REDUCE函数那种返回最后一行的功能。 另外,SCAN/REDUCE这两个函数还有一个累加器功能,尽管我们在示例中没有使用,但是必要时它们可以发挥巨大作用。BYROW就没有这个能力。 也有朋友在之前的For循环文章下留言,问既然没有用到acc这个参数,为什么不用MAP函数。 原因也是一样的。SCAN/REDUCE函数的适用范围更广,如果不使用累加器功能,它就像普通的MAP一样,但是必要时,累加器就可以起到大作用。 所以,为了应用范围更广,建议使用SCAN/REDUCE函数。 最后还有一个问题: 能否使用这个“For循环函数”进行双重循环,类似于下面这种形式: ForLoop1(ForLoop1(......)......) |
|