使用三元运算符进行递归' hack'在Python中

时间:2013-11-08 13:05:49

标签: python recursion conditional-operator

在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]

这将超过最大递归深度。

为什么这不起作用?

(我知道两者都不是一个很好的编码风格的例子,但现在不是重点。)

3 个答案:

答案 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,然后执行 AB 的结果如果您执行[B, A][C],则执行AB然后 C。您可以通过p("A") if p("C") else p("B")[p("B"), p("A")][p("C")]使用打印输入的某个函数p轻松检查这一点,然后返回TrueFalse

因此,在您的第一种情况S if len(S)==b else f(S[1:],b)中,仅当条件不适用时才执行递归调用。然而,在第二种情况下,它在条件被测试之前执行 ,并且在递归调用的函数中是相同的,无限制地。

(我假设你不打算在实践中使用它,所以这可能不重要,但无论如何:注意(1)两个函数都缺乏对案例len(S) < b的保护,(2)使用S[-b:]可以实现同样的目标,而(3)使用if/else当然,很多更具可读性。)