【问题标题】:How can I convert this for loop pseudocode into Python code without inducing infinite recursion?如何在不引发无限递归的情况下将此 for 循环伪代码转换为 Python 代码?
【发布时间】:2017-11-06 19:46:44
【问题描述】:

我想把下面的伪代码转换成 Python 代码,而不引起无限递归。

u(x) = x^2

for i = 0 to 5
v(x) = u(x)^2
u(x) = v(x)

print(u(x))

这是我的尝试,它会产生错误“超出最大递归深度”:

import sympy as sym

u = lambda a: a**2

for i in range(0,5):
  print('Starting loop',i)
  v = lambda b: u(b)**2
  u = lambda c: v(c)
  print('Ending loop',i)

x = sym.symbols('x')
print(u(x))

【问题讨论】:

  • 一个未指明的错误”,指定它。一定有什么东西生产出来的。
  • 补充,别删了再问(现删:stackoverflow.com/questions/44378964/…
  • 确实,编辑您的问题并添加错误消息。这将使其他人更容易提供帮助。
  • 您需要在您的函数伪代码中添加一个计数器 i 并每次递减,循环不会影响定义。也就是说,它不是伪代码,只是一些伪伪代码?

标签: python python-3.x for-loop recursion sympy


【解决方案1】:

尝试使用这两行代码:

v = lambda b, u=u: u(b)**2
u = lambda c, v=v: v(c)

这会强制在构造 lambda 时捕获 uv 的值。否则,评估将推迟到 lambda 被调用。

这是完整的程序:

import sympy as sym

u = lambda a: a**2

for i in range(0,5):
  print('Starting loop',i)
  v = lambda b, u=u: u(b)**2
  u = lambda c, v=v: v(c)
  print('Ending loop',i)

x = sym.symbols('x')
print(u(x))

结果如下:

$ python3 xx.py 
Starting loop 0
Ending loop 0
Starting loop 1
Ending loop 1
Starting loop 2
Ending loop 2
Starting loop 3
Ending loop 3
Starting loop 4
Ending loop 4
x**64

【讨论】:

  • 是的,这正是闭包捕获的内容。它们捕获封闭范围变量的名称,而不是值。
  • 确实,Python 并不是真正的函数式语言。也许像访问 func_code 这样的黑魔法,而不是函数本身可能会有所帮助
  • @user46944 词法范围的闭包适用于lambda 函数和普通的def 定义函数。词法作用域一点也不奇怪,它几乎在我所知道的所有主要语言中都有使用。
  • @Serge 什么?不,这根本不是你应该做的。请注意闭包的语义并适当地使用它们。
【解决方案2】:

Python 变量是按名称绑定的,for 循环实际上并没有创建新的作用域。因此,当您执行lambda c: v(c) 时,实际上是在创建一个函数,该函数将在执行时从其周围范围查找v。这意味着对v 的更新都会在函数执行时应用。

特别是,这意味着下面两个定义已经创建了一个无限循环:

v = lambda b: u(b)**2
u = lambda c: v(c)

因为v 调用u,而u 调用v。稍后更新值无关紧要,因为在调用函数时会查找值。

您可以使用以下方法轻松地对此进行可视化:

>>> x = lambda: y
>>> y = 2
>>> x()
2
>>> y = 5
>>> x()
5

即使函数x 从未更新,它仍将使用y 的更新值。

你需要的是一个 闭包 将原始函数的 get 引用放在一个单独的范围内,以便以后的更改不会影响函数。一种简单的方法是添加另一个函数,将要调用的函数作为参数传递。由于函数会创建新的变量作用域,因此这些作用域将独立于原始定义:

for i in range(0, 5):
    print('Starting loop', i)
    v = (lambda u: lambda b: u(b)**2)(u)
    u = (lambda v: lambda c: v(c))(v)
    print('Ending loop', i)

另请参阅how binding works 上的这个问题以及闭包如何帮助。

【讨论】:

  • 是的,将函数闭包包装在另一个函数中是使用后期绑定处理词法范围的规范方法。但是,在 Python 中使用关键字参数 hack 可以强制“提前绑定”,而且它在我看来更具可读性。虽然,它确实依赖于公共语言陷阱(关键字参数在定义时评估)来处理另一种公共语言陷阱(后期绑定,词法范围的闭包)。
猜你喜欢
  • 2022-10-13
  • 1970-01-01
  • 1970-01-01
  • 2023-01-22
  • 1970-01-01
  • 1970-01-01
  • 2015-08-22
  • 2022-11-12
  • 2021-10-22
相关资源
最近更新 更多