如何在不引入无限递归的情况下将此for循环伪代码转换为Python代码?

时间:2022-02-12 07:52:44

I would like to turn the following pseudocode into Python code without inducing infinite recursion.

我想将以下伪代码转换为Python代码而不会引发无限递归。

u(x) = x^2

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

print(u(x))

Here is my attempt, which produces the error "maximum recursion depth exceeded":

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

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))

2 个解决方案

#1


2  

Try using these two lines:

尝试使用这两行:

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

This forces the values of u and v to be captured at the moment of lambda construction. Otherwise, the evaluation will be deferred until the lambdas are invoked.

这迫使在λ构造时捕获u和v的值。否则,将推迟评估,直到调用lambda。

Here is the complete program:

这是完整的程序:

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))

And here is the result:

这是结果:

$ 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

#2


3  

Python variables are bound by name, and a for loop does not actually create a new scope. So when you do lambda c: v(c), you are actually creating a function that will look up v from its surrounding scope when its being executed. This means that updates to v are all applied when the function is executed.

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

In particular, it means that the following two definition already create an infinite loop:

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

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

Because v calls u, and u calls v. It does not matter that the values are updated later, since the value will be looked up when the function is called.

因为v调用u,并且u调用v。稍后更新值并不重要,因为在调用函数时将查找该值。

You can visualize this easily using the following:

您可以使用以下内容轻松地将其可视化:

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

Even though the function x is never updated, it will still use the updated value for y.

即使函数x永远不会更新,它仍将使用y的更新值。

What you need here is a closure to put get references to the original functions in a separate scope so that later changes do not affect the function. A simple way is to add another function where the function you want to call is passed as an argument. Since functions create new variable scopes, these will be then independent of the original definitions:

这里需要的是一个闭包,用于在单独的作用域中对原始函数进行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)

See also this question on how binding works and how closures help there.

另请参阅关于绑定如何工作以及闭包如何帮助的问题。

#1


2  

Try using these two lines:

尝试使用这两行:

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

This forces the values of u and v to be captured at the moment of lambda construction. Otherwise, the evaluation will be deferred until the lambdas are invoked.

这迫使在λ构造时捕获u和v的值。否则,将推迟评估,直到调用lambda。

Here is the complete program:

这是完整的程序:

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))

And here is the result:

这是结果:

$ 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

#2


3  

Python variables are bound by name, and a for loop does not actually create a new scope. So when you do lambda c: v(c), you are actually creating a function that will look up v from its surrounding scope when its being executed. This means that updates to v are all applied when the function is executed.

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

In particular, it means that the following two definition already create an infinite loop:

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

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

Because v calls u, and u calls v. It does not matter that the values are updated later, since the value will be looked up when the function is called.

因为v调用u,并且u调用v。稍后更新值并不重要,因为在调用函数时将查找该值。

You can visualize this easily using the following:

您可以使用以下内容轻松地将其可视化:

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

Even though the function x is never updated, it will still use the updated value for y.

即使函数x永远不会更新,它仍将使用y的更新值。

What you need here is a closure to put get references to the original functions in a separate scope so that later changes do not affect the function. A simple way is to add another function where the function you want to call is passed as an argument. Since functions create new variable scopes, these will be then independent of the original definitions:

这里需要的是一个闭包,用于在单独的作用域中对原始函数进行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)

See also this question on how binding works and how closures help there.

另请参阅关于绑定如何工作以及闭包如何帮助的问题。