在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]。
然而,令我惊讶的是,以下解决方案使用'三元黑客'[a,b] [c]而不是“b if c else a”(又名“c?b:a”)不起作用:
g = lambda S,b : (g(S[1:],b),S)[len(S)==b]
这将超过最大递归深度。
为什么这不起作用?
(我知道两者都不是一个很好的编码风格的例子,但现在不是重点。)
答案 0 :(得分:6)
好的,让我们看一下lambda函数生成的ast
:
import ast
tree = ast.parse('lambda S,b : (g(S[1:],b),S)[len(S)==b]')
ast.dump(tree)
在vim中完成一些格式化后,这就是我所得到的:
Module(
[Expr(
Lambda(
arguments(
[Name('S', Param()), Name('b', Param())],
None,
None,
[]
),
Subscript(
Tuple(
[Call(
Name('g', Load()),
[Subscript(Name('S', Load()), Slice(Num(1), None, None), Load()), Name('b', Load())],
[],
None,
None
),
Name('S', Load())
],
Load()
),
Index(
Compare(
Call(Name('len', Load()), [Name('S', Load())], [], None, None),
[Eq()],
[Name('b', Load())]
)
),
Load()
)
)
)]
)
正如您所看到的,此代码在调用lambda时执行的第一件事是元组创建,之后,直接将递归调用(Call(Name('g'...
)传递给同一个lambda。
调用是第一件事,因为切换空列表仍然是空列表:
>>>[1][1:]
[]
>>>[][1:]
[]
这意味着g(S[1:])
会将列表缩小到空列表,然后继续用空列表无休止地调用g
。这是因为解析器执行语句的方式。执行的第一件事是递归方法调用,所以它不会停止。
我的观点:基本情况不适用于递归。
希望这能为这个话题带来更多启发。
答案 1 :(得分:5)
我认为问题与三元运算符的工作原理有关。当您使用三元运算符时,在检查条件之前会对两个表达式进行求值。
g = lambda S,b : (g(S[1:],b),S)[len(S)==b]
因此,在这种情况下,g(S[1:],b)
在到达if语句之前会被评估。
如果你有一个函数,那么没有基本情况与g(S[1:],b)
def func(S, b)
return func(S[1:],)
func(S,b)
#output: error - exceed maximum recursion depth
S [1:]将达到空的点,如果它为空,它将返回一个空列表。
关于空列表的一个小例子:
S = [0, 1]
S = S[1:]
# [1]
S = S[1:]
# [] # empty
S = S[1:]
# [] # also empty
答案 2 :(得分:2)
如果您执行A if C else B
,则首先执行C
,然后执行 A
或B
的结果如果您执行[B, A][C]
,则执行A
和B
,然后 C
。您可以通过p("A") if p("C") else p("B")
和[p("B"), p("A")][p("C")]
使用打印输入的某个函数p
轻松检查这一点,然后返回True
或False
因此,在您的第一种情况S if len(S)==b else f(S[1:],b)
中,仅当条件不适用时才执行递归调用。然而,在第二种情况下,它在条件被测试之前执行 ,并且在递归调用的函数中是相同的,无限制地。
(我假设你不打算在实践中使用它,所以这可能不重要,但无论如何:注意(1)两个函数都缺乏对案例len(S) < b
的保护,(2)使用S[-b:]
可以实现同样的目标,而(3)使用if/else
当然,很多更具可读性。)