Python中三元运算符hack的递归

2024-04-28 15:37:35 发布

您现在位置:Python中文网/ 问答频道 /正文

在Python中玩了一下,我发现以下代码如我所料地工作:

f = lambda S,b : (S if len(S)==b else f(S[1:],b))

从列表S,它将递归地删除第一个元素,直到S的长度等于b。例如f([1,2,3,4,5,6],3)=[4,5,6]。在

然而,令我大吃一惊的是,下面的解决方案使用了“三元hack”[a,b][c]而不是“b if c else a”(又名“c”?b: a“)不起作用:

^{pr2}$

这将超过最大递归深度。在

为什么这个不管用?在

(我知道这两个都不是优秀的编码风格的例子,但这与重点无关。)


Tags: lambda代码元素重点编码列表lenif
3条回答

好,让我们看看lambda函数生成的ast

import ast
tree = ast.parse('lambda S,b : (g(S[1:],b),S)[len(S)==b]')
ast.dump(tree)

在vim中完成一些格式化后,我得到了:

^{pr2}$

如您所见,在调用lambda时,代码执行的第一件事是创建元组,然后直接对同一lambda执行递归调用(Call(Name('g'...)。在

首先要做的是调用,因为空列表的切片仍然是空列表:

>>>[1][1:]
[]
>>>[][1:]
[]

这意味着g(S[1:])将减少您的列表,直到空列表,然后继续用空列表不断调用g。这是因为解析器执行语句的方式。首先执行的是递归方法调用,因此它不会停止。在

我的观点是:基本情况不适用于递归。在

希望这能给这个问题带来更多的启发。在

如果执行A if C else B,则先执行C,然后执行其中一个A或{}中的一个(并返回结果),而如果执行[B, A][C],则执行A和{},然后执行C。您可以通过使用某个函数p("A") if p("C") else p("B")[p("B"), p("A")][p("C")]来轻松检查这一点,该函数打印其输入,然后返回True或{}

所以在第一种情况下,S if len(S)==b else f(S[1:],b),递归调用只在条件不适用的情况下执行。然而,在第二种情况下,它甚至在条件被测试之前被执行,在递归调用的函数ad infinium中也是如此。在

(我假设您不打算在实践中使用它,因此这可能并不重要,但无论如何:请注意:(1)这两个函数都缺少对case len(S) < b的保护,(2)使用S[-b:]可以实现相同的效果,并且(3)使用if/else当然,更具可读性。)

我认为问题与三元运算符的工作原理有关。当使用三元运算符时,在检查条件之前将对两个表达式求值。在

g = lambda S,b : (g(S[1:],b),S)[len(S)==b]

因此在本例中,g(S[1:],b)甚至在到达if语句之前就得到了求值。在

如果您有一个函数,那么没有与g(S[1:],b)相同的基本情况

^{pr2}$

S[1:]将到达它为空的点,如果它是空的,它将返回一个空列表。在

关于空列表的一个小例子:

S = [0, 1]

S = S[1:]
# [1]

S = S[1:]
# [] # empty

S = S[1:]
# [] # also empty

相关问题 更多 >