ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 6926|回复: 20

[原创] LAMBDA函数:递归基础

[复制链接]

TA的精华主题

TA的得分主题

发表于 2022-11-24 18:58 | 显示全部楼层 |阅读模式
本帖已被收录到知识树中,索引项:LAMBDA
本帖最后由 shaowu459 于 2023-12-13 22:48 编辑

递归是一种在循环时不断调用自身的常见编程技巧,如果一个问题可以抽象为不断按照同一个模式运算,则可以考虑使用递归来解决这个问题。
图片.jpg


递归技巧在各种编程语言中都可以使用,就Excel来说,可以在VBA、Power Query等中直接使用。但在Excel工作表函数中之前是无法实现递归运算的,这种情况直到LAMBDA函数的出现得以改变。

LAMBDA函数是Office 365版本中出现的新函数,这个函数允许用户在工作表操作界面中像VBA那样创建自定义函数,因为自定义函数的存在进而使调用自定义函数自身的递归方法实现成为了可能。
图片.png

关于LAMBDA函数的详细介绍,可以参考满坛版主的精华帖:[分享] 365新函数LAMBDA,在此不做过多说明,本文重点介绍如何使用LAMBDA函数实现递归运算。

在Excel中使用LAMBDA实现递归有两个基本要求:
一是使用LAMBDA函数制作的自定义函数必须定义为名称;
二是必须为LAMBDA函数定义的自定义函数设置退出条件,因为本质上递归也是循环,如果不设置退出条件循环将无法终止。

下面我们通过两个例子来说明如何使用LAMBDA函数实现递归运算。


补充说明:在名称管理器中定义LAMBDA自定义函数使用递归的时候,使用IFS函数会出错,目前判断是个bug,多个退出条件判断可以嵌套使用IF函数。


LAMBDA递归基础.rar

111.46 KB, 下载次数: 229

评分

7

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2023-9-25 08:41 | 显示全部楼层
关于递归出现错误的调试方法:
递归公式一般逻辑很简单,在满足条件的情况下终止返回某值,否则调整参数后调用自身进入下一次循环。在这种情况下,一般出现了错误是比较难以排查的。下面介绍两种可能得调试和处理方法,以fx=lambda(x,y,if(终止条件,终止返回值,fx(x',y')))为例(简化了结构,实际可能会有REDUCE函数堆积的过程,这样才能显示中间值出来):

方法一:添加计数器
fx中添加一个新参数,用于计数,例如在公式中添加z这个参数,每次调用的时候z值+1:
fx=lambda(x,y,z,if(终止条件,终止返回值,fx(x',y',z+1)))
如果原来的递归过程中出错,上面的公式还会出错,但是可以使用IFERROR函数判断再次调用的fx出错时,返回z的值,这样就知道是第几次递归调用时出错。还有一个应用,就是假如设定递归100次后还没有结果,就让返回“超时,无结果”这种,可以用z作为退出条件,也就是达到一定次数就终止递归。

方法二:输出出错时的x或y值,或者关于x或y的统计值、字符串

假设fx公式不变,仍为以下公式:
fx=lambda(x,y,if(终止条件,终止返回值,fx(x',y')))
循环时x和y可能是单值也可能是数组,可能是数字也可能是其他值。可以使用IFERROR函数返回当递归调用fx出错时的值,例如返回x或y本身,或者CONCAT(x)或CONCAT(y),也可能是COUNT函数返回x或y的数字个数,也可能是其他字符串,类似:"出错时x中元素的数量是"&COUNTA(x)之类的,用于辅助判断。

评分

1

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2024-1-2 14:27 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
关于递归函数的使用提示:

在工作表函数公式中使用递归方法,不一定使用递归的本意,也即不一定使用一层一层往下迭代这个用法。例如,在使用REDUCE函数解决问题时,可能要用到原始数组,同时在循环过程中要对原始数组进行提取、运算后形成新的结果数组,并且原数组可能要再剔除或增加某些行以便进行下次运算。在这种情况下,既需要原始数组又需要结果数组,如果堆叠起来在后续运算时再拆分合并就比较麻烦了。这时,可以用递归来代替这种循环,在递归函数fx里设置不同的参数,不同的参数在每次循环时各自可以进行维护和修改,包括增删行、增减字符串长度、横纵向堆叠、增加或减少计数等等,每个参数都可以是不同维度、不同类型的,各自运算和维护。这种方法能实现编程时使用的各种临时数组、原始数组、结果数组、计数器、字符串、中间结果等等的维护和运算。


例如,fx可以写成类似下面的形式:
fx(原数组array,计数器n,结果数组res,结果字符串res,中间m*n的数组1,中间m*n的数组2,中间判定字符串str,中间变量1,中间变量2,状态值1,状态值2,累计求和值……)


上面的递归公式运算时,每次都可以对原数组array进行运算和更新(每次循环后array可能都会发生变化),满足条件就更新结果数组或字符串的值。这样,只要设定好退出条件,例如计数器n达到了原字符串的长度,或者array仅剩余1行,或者结果字符串长度达到原字符串长度等等,就可以方便的利用fx中存储的各种信息来进行运算和返回值。

TA的精华主题

TA的得分主题

 楼主| 发表于 2024-3-14 13:08 | 显示全部楼层
群里朋友分享的在excel单元格公式中直接使用递归的方法,不用定义名称。

在LET中定义fx,其中LAMBDA函数部分参数使用fx作为参数,后面嵌套调用。例如求1-100的合计:
  1. =LET(fx,LAMBDA(fx,n,IF(n=1,1,n+fx(fx,n-1))),fx(fx,100))
复制代码
图片.png

评分

1

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2024-5-7 11:52 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
递归小例题:
  1. =LET(fx,LAMBDA(fx,n,IF(n=1,1,VSTACK(n,n&"+"&fx(fx,n-1)&"+"&n,n))),fx(fx,10))
复制代码
图片.jpg

循环也大概写了一个,供参考:

  1. =MAP(ROW(1:19),LAMBDA(x,LET(s,SEQUENCE(IF(x>10,20-x,x),,10,-1),TEXTJOIN("+",,s,IFERROR(DROP(SORTBY(s,s),1),"")))))
复制代码
图片.jpg




TA的精华主题

TA的得分主题

 楼主| 发表于 2022-11-24 19:13 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
本帖最后由 shaowu459 于 2022-11-25 09:24 编辑

示例一:求菲波那契数列第10项的值。

斐波那契数列指的是这样一个数列:1、1、2、3、5、8、13、21、34、……从第3项起,每项的值为其前两项的值合计,例如2=1+1,3=2+1,5=3+2,以此类推。抽象出来的表达式为:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3)。可以观察到,每一项都等于前两项的合计,这就是一个固定的运算模式,因此可以使用递归来完成。
图片.png
举例来说,求第5项是多少,图解如下:
图片.png

从上往下到步骤4的时候,F(2)和F(1)的已经是确定的了,因此F(3)的值也就确定了:F(3)=F(2)+F(1)=1+1=2
F(3)确定了那么F(4)也就确定了:F(4)=F(3)+F(2)=2+1=3
F(4)确定了那么F(5)也就确定了:F(5)=F(4)+F(3)=3+2=5。

下面来详细描述一下使用LAMBDA函数递归来解决这个问题的详细步骤。首先,在名称管理器中定一个名称FX,其公式如下:
  1. =LAMBDA(x,IF(x<3,1,FX(x-1)+FX(x-2)))
复制代码

图片.png
上述公式的意思是,如果给定项的值小于3,也就是为1或2,公式都将返回1,因为数列第1和第2项值都是1。如果给定项的值大于等于3,则返回这一项编号减去1和减去2的项,也即当前项前1项和前2项的值。以此类推,一直到x小于3时停止循环。

在工作表任意空单元格中输入=FX(10)则返回菲波那切数列第10项的值:
图片.png

评分

1

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2022-11-24 19:32 | 显示全部楼层
本帖最后由 shaowu459 于 2022-11-25 09:17 编辑

示例二:根据父级ID返回部门全称。

如下图,B列是部门名称,A列是部门的id,C列是部门的父级部门id,要求在D列返回部门的全称。如【南京办】的父级ID是3,也就是【销售部】,【销售部】父级id是1,也就是【营销中心】,因此【南京办】的部门全称是:【营销中心 / 销售部 / 南京办】。
图片.png

递归解决这个问题的思路是:针对每一个部门,都去查其父级id,如果父级id有数字,就用【查找父级id返回的字符串】&"/"&当前部门名称,直到父级id是空单元格(没有上级id)为止。

以【南京办】这个部门为例,从查找其父级id数字3开始(H2单元格处的“福州办”笔误,应为“南京办”):
图片.jpg

最终查找id=1返回【营销中心】,然后再逐层向上返回值。

使用LAMBDA函数实现的具体方式为:单击任意单元格定义名称,定义一个名称GX,其公式如下:
  1. =LAMBDA(x,IF(x=0,OFFSET(x,,-1),GX(XLOOKUP(x,$A:$A,$C:$C))&" / "&OFFSET(x,,-1)))
复制代码
图片.png

该公式的意思是:
如果父级id列值是0(空单元格),也就是没有上级部门了,就返回父级id左侧1列的单元(也即部门名称),这是退出循环的条件。如果父级id列值大于0,也就是有上级部门则再次调用GX,其参数为在A列中查找当前的父级id,返回上级部门对应的父级id,继续按上述模式运算,并且将返回值与当前行的部门名称用斜杠连接起来。

D6单元格输入公式:=GX(C6),将公式下拉即可返回每一个部门的全称。
图片.png

评分

1

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2022-11-24 19:38 | 显示全部楼层
本帖最后由 shaowu459 于 2022-11-24 19:42 编辑

以上就是使用LAMBDA函数实现递归的两个基本例子,在使用时注意一定要设置退出条件。

下面还有一个用递归方法将不同列数据的全部组合列示出来的小题,如下图,要求返回款号、部位、颜色、床次、尺码、搭配这几列内容的全部组合。这个小题就不再单独解释了,如果有问题的话可以跟帖讨论。
图片.jpg

评分

1

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2022-11-24 21:20 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2022-11-27 23:13 | 显示全部楼层
示例二也可以这样
=IFNA(VLOOKUP(C6,A$1:D5,4,)&"/"&B6,B6)
都是体现递归的思想

评分

1

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2022-11-27 23:59 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
数论 发表于 2022-11-27 23:13
示例二也可以这样
=IFNA(VLOOKUP(C6,A$1:D5,4,)&"/"&B6,B6)
都是体现递归的思想

对,下拉公式,充分利用上方已生成的结果。

TA的精华主题

TA的得分主题

发表于 2022-11-28 10:46 | 显示全部楼层
shaowu459 发表于 2022-11-24 19:32
示例二:根据父级ID返回部门全称。

如下图,B列是部门名称,A列是部门的id,C列是部门的父级部门id,要 ...

太强大了啊

TA的精华主题

TA的得分主题

发表于 2022-12-5 12:37 | 显示全部楼层
shaowu459 发表于 2022-11-24 19:13
示例一:求菲波那契数列第10项的值。

斐波那契数列指的是这样一个数列:1、1、2、3、5、8、13、21、34、 ...
  1. def fibonacci(n):
  2.     if n == 0:
  3.         return 0
  4.     elif n == 1:
  5.         return 1
  6.     else:
  7.         return fibonacci(n-1) + fibonacci(n-2)

  8. print(fibonacci(10))
复制代码

TA的精华主题

TA的得分主题

发表于 2022-12-7 09:12 | 显示全部楼层
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-11-16 06:02 , Processed in 0.055718 second(s), 15 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表