在while循环中修改变量的递归函数 [Python]

3

我有一个学生给了我以下代码:

def addtwo():
 numb=input("enter any number")
 add=int(numb)+2
 print(add)
 inp = input("do you wish to enter again")
 while inp=="yes":
       addtwo()

第一次迭代后,如果用户输入的不是“yes”,那么while循环体不会执行,这是预期的。但如果我们输入“yes”,它将执行(同样是预期的),并提示“输入任何数字”,但在第二次迭代中,即使用户在“您是否希望再次输入”上输入了其他内容,while循环体仍然会执行。现在我对其进行了调试,并发现当执行第5行时,inp的值就会改变为'yes'。在执行该行之前,根据调试器显示,inp的值仍然是用户输入的任何内容。为什么会这样呢?
我在这里查找了资料,但没有找到解释,尽管我在这里找到了一种方法,可以在while循环体中调用addtwo()之前添加一个返回(我在这里找到了它:Calling a function recursively for user input),但我不明白为什么inp的值会在本地堆栈中更改,以及return语句如何修复它?
下面是可工作的代码:
def addtwo():
     numb=input("enter any number")
     add=int(numb)+2
     print(add)
     inp = input("do you wish to enter again")
     while inp=="yes":
           return addtwo()

而且增加了我的困惑,如果我们使用if语句而不是while语句,则代码可以正常工作。


2
如果 inp=="yes",那么 while 循环就是无限的。循环内部没有任何东西改变局部变量 inp 的值。在第二个版本中,return 通过结束函数来退出无限循环。 - khelwood
3
您正在查看两个不同的“inp”变量; 一个来自您的递归调用,然后当该调用完成时,它回到您的主调用,其中“inp”最初是“yes”。由于它仍然是“yes”,因此循环。 - Davy M
非常感谢您抽出时间回复,@khelwood和@Davy M:但是如果“inp =='yes'”语句使“while”循环无限,那么这段代码如何工作:
'myChoice ='y' while myChoice == 'y': myNumber = input('Please enter your number') print(int(myNumber)+2) myChoice = input('Do you want to add another number [y/n]')
- Umair Rafique
@UmairRafique 不要试图将代码放在注释中。对于Python,缩进对代码的行为至关重要。 - khelwood
2个回答

4

概述

这是因为一旦您输入了“是”一次,该函数将无限循环,而不管下一个递归调用做什么。

您可以通过更改以下内容来修复您的代码:

while inp == "yes":
    add_two()

为了

if inp == "yes":
    add_two()

步骤概述

  • 调用add_two函数
  • 用户输入一个数字
  • 询问用户是否继续
  • 用户输入"yes"
  • while循环条件imp == "yes"始终为真
  • 调用add_two
    • 用户输入一个数字
    • 用户输入"no"
    • 循环不执行,返回到第一次调用add_two
  • 回到while循环,条件仍为真,继续调用add_two

返回语句

在Python中,默认情况下,没有返回值的函数实际上返回None。这意味着如果用户在提示时未输入"yes",则会返回,这将强制第一次调用也返回。


我刚才想起来了,'if'语句是有效的。但是很抱歉,我还是不太清楚。在你的步骤中,如果循环没有运行,为什么会返回到第一个调用呢? - Umair Rafique
1
@UmairRafique 当你从另一个函数中调用一个函数时,第一个函数会在调用发生的地方暂停,并等待第二个函数。第二个函数运行其代码,当它完成后,返回到第一个函数调用,然后继续从它调用第二个函数的位置之后继续执行。 - dangee1705
1
@UmairRafique 所以如果在第二个函数调用中,inp不是“yes”,那么循环将不会运行,因为inp == "yes"将为false。因此,该函数将返回到第一个。 - dangee1705
1
@UmairRafique 希望这样说得清楚。另外,如果我的答案解决了你的问题,请接受它,这样其他有同样问题的用户也可以得到帮助。 - dangee1705
抱歉,我仍然不明白为什么在这里使用if而不是while会很好。因为它第一次执行时也是true,然后递归调用函数,这也应该意味着它已经创建了另一个主函数的副本,如果该副本返回false,则仍然第一次调用函数的值已经设置为Yes。那么它们有什么不同? - Pradhumn Sharma
显示剩余2条评论

2
调用递归函数有点像复制和粘贴内部函数一样。复制/粘贴和真正的递归之间存在一些差异:
  • 每个函数都有自己的变量集
  • 当你将一个函数复制并粘贴到它本身时,该函数只能调用(一个副本)自己多次,而递归会自动深入任意深度。(这就是递归的全部意义。)
尽管存在这些差异,如果你难以理解递归调用,复制和粘贴是一种很好的思考方式。让我们为你的函数尝试一下。以下代码等效于你编写的代码,只要你不输入太多的“yes”即可:
def addtwo0():
     numb=input("enter any number")
     add=int(numb)+2
     print(add)
     inp = input("do you wish to enter again")
     while inp=="yes":
           return addtwo1()

def addtwo1():
     numb=input("enter any number")
     add=int(numb)+2
     print(add)
     inp = input("do you wish to enter again")
     while inp=="yes":
           return addtwo2()

def addtwo2():
     numb=input("enter any number")
     add=int(numb)+2
     print(add)
     inp = input("do you wish to enter again")
     while inp=="yes":
           raise Exception("tried to recurse too deeply!")

如果我们将这些内容放在一个函数中,可能会更加清晰。我们需要重新命名变量,以免它们互相覆盖。

def addtwo():
     numb0=input("enter any number")
     add0=int(numb0)+2
     print(add0)
     inp0 = input("do you wish to enter again")
     while inp0=="yes":
         numb1=input("enter any number")
         add1=int(numb1)+2
         print(add1)
         inp1 = input("do you wish to enter again")
         while inp1=="yes":
             numb2=input("enter any number")
             add2=int(numb2)+2
             print(add2)
             inp2 = input("do you wish to enter again")
             while inp2=="yes":
                 raise Exception("tried to recurse too deeply!")

现在你可以看到问题的直接原因:将inp2设置为“yes”并不会将inp1imp0设置为“yes”。或者在原始代码中,在对addtwo进行嵌套调用时,将inp设置为“yes”不会将外部的imp设置为“yes”,因为每个函数都有其自己的变量集。
你现在也可以看到问题的根本原因:你不需要多个循环来完成这个任务。递归(带有一个if)或一个while循环足以使条件重复检查。如果同时使用两种方法,即使解决了直接问题,也会使事情变得不必要地复杂。在这种情况下,while循环实际上是最简单的方法;将input语句放入其中,并完全摆脱递归调用。

谢谢你解释得这么好。现在我只有一个问题:第一个函数(我称之为根,其他的称之为分支)似乎停留在第一个while的主体,并调用分支(对吗?),当分支的while主体没有被执行时,它返回到根,但为什么if块不会这样做呢?我明白了你使用while循环而不是递归调用的意思(那就是我提出的解决方案),但我只是想理解这种行为。 - Umair Rafique
没事了,我想我明白了。再次感谢。 - Umair Rafique
抱歉,我仍然不明白在这里使用if而不是while如何能正常工作。因为它第一次也是作为true被执行,然后递归调用函数,这也应该意味着它已经创建了另一个主函数的副本,如果那个副本返回false,那么第一个调用函数仍然已经将值设置为Yes。那么它们有什么区别? - Pradhumn Sharma
如果你使用if而不是while(并保持递归),那么是的,你是正确的,inp的外部副本(你描述为“第一个调用函数...值”)仍将设置为“是”。但这没关系,因为外部函数的if测试已经运行过了,永远不会再次运行,所以它的值已经不重要了(而while会循环回来再次检查它)。 - Arthur Tacca

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接