将含多语句的Python函数转为lambda并结合Y组合器改写求助
解决Python递归函数转Lambda适配Y组合器的问题
嘿,我懂你现在的困扰——lambda表达式只能包含单个表达式,没法直接放赋值、多条语句,这确实是新手把递归函数转成lambda适配Y组合器时的常见卡点。咱们一步步拆解这两个函数,把它们改成符合lambda语法的形式,之后就能对接Y组合器啦。
先搞定 wf_print 函数
原函数的逻辑很清晰:如果列表为空就返回,否则解包第一个元素打印,再递归处理剩余部分。你之前尝试用列表的问题在于列表里不能放赋值语句,而且lambda只能有一个表达式。咱们可以用以下技巧绕过这个限制:
基础Lambda版本(命名递归)
wf_print = lambda wordfreq: None if not wordfreq else (lambda w, c: print(w, '-', c) or wf_print(wordfreq[1:]))(*wordfreq[0])
这里的关键细节:
- 用
not wordfreq替代wordfreq == [],更简洁且符合Python风格 - 定义临时lambda接收解包后的
w和c,先执行print()(返回None),再用or触发递归调用(因为None or x会执行并返回x) - 用
*wordfreq[0]把元组wordfreq[0]解包成两个参数传给临时lambda
适配Y组合器的版本(匿名递归)
要对接Y组合器,得把函数自身作为参数传入,去掉对wf_print名字的直接引用:
wf_print = lambda f, wordfreq: None if not wordfreq else (lambda w, c: print(w, '-', c) or f(f, wordfreq[1:]))(*wordfreq[0])
接下来处理 count 函数
这个函数的难点是修改wordfreqs字典的赋值操作——lambda里不能直接写赋值语句,咱们可以用字典的__setitem__方法(这是一个函数调用,属于合法表达式)来替代:
原逻辑里的:
if word in wordfreqs: wordfreqs[word] += 1 else: wordfreqs[word] = 1
可以转成这个表达式:
wordfreqs.__setitem__(word, wordfreqs.get(word, 0) + 1)
它会直接修改字典,返回None,但完全符合lambda的语法要求。
基础Lambda版本(命名递归)
count = lambda word_list, stopwords, wordfreqs: None if not word_list else ( lambda word: wordfreqs.__setitem__(word, wordfreqs.get(word, 0) + 1) if word not in stopwords else None or count(word_list[1:], stopwords, wordfreqs) )(word_list[0])
解释一下:
- 先判断
word_list是否为空,为空返回None - 不为空就取出第一个
word传给临时lambda - 临时lambda判断
word是否不在停用词里:是就执行__setitem__修改字典,否则返回None - 最后用
or触发递归调用,不管前面的操作结果是什么,都会执行后续的递归
适配Y组合器的版本(匿名递归)
同样,要去掉对count名字的直接引用,把函数自身作为参数传入:
count = lambda f, word_list, stopwords, wordfreqs: None if not word_list else ( lambda word: wordfreqs.__setitem__(word, wordfreqs.get(word, 0) + 1) if word not in stopwords else None or f(f, word_list[1:], stopwords, wordfreqs) )(word_list[0])
最终对接Y组合器
Y组合器的作用是让匿名函数实现递归,针对多参数函数,我们可以用这个通用的Y组合器实现:
Y = lambda f: lambda *args: f(lambda *args2: Y(f)(*args2), *args)
然后把我们的匿名函数用Y组合器包裹,就得到了无需命名的递归函数:
最终的count函数
count = Y(lambda f, word_list, stopwords, wordfreqs: None if not word_list else ( lambda word: wordfreqs.__setitem__(word, wordfreqs.get(word, 0) + 1) if word not in stopwords else None or f(word_list[1:], stopwords, wordfreqs) )(word_list[0]))
最终的wf_print函数
wf_print = Y(lambda f, wordfreq: None if not wordfreq else (lambda w, c: print(w, '-', c) or f(wordfreq[1:]))(*wordfreq[0]))
关键注意点
- Python的lambda没有“语句表达式”特性(不像Scheme这类函数式语言),所以我们只能用
__setitem__、and/or这类技巧来模拟多语句的执行 - 转写时核心是把所有操作都压缩成单个表达式,同时处理好递归调用的引用问题
内容的提问来源于stack exchange,提问作者Quentin
相关产品推荐
相关产品推荐

