【问题标题】:Python loop iterates after expected terminationPython循环在预期终止后迭代
【发布时间】:2020-06-04 04:24:59
【问题描述】:

下面的 Python 代码有一个奇怪的行为,我无法弄清楚。

程序调用 testQuery,它要求用户回答“是”以调用 scoreAverager,或“否”以退出程序。如果调用 scoreAverager,它会要求用户输入一系列分数,或“x”来完成,这会将用户返回到 testQuery,在那里他们可以选择对另一个测试进行平均或退出。

如果用户对多个测试(例如 2 次或更多)的结果进行平均,就会发生奇怪的事情。 那时,对 testQuery 给出“否”将无法终止程序。它将为每个平均测试迭代一个额外的循环。我不明白为什么会这样。它看起来和行为都像一个错误的错误,但关闭循环的条件似乎已经满足。可以通过'break'来解决,但我更想知道问题是什么才能更有机地解决它。

谁能告诉我为什么会发生这种奇怪的行为?

代码:

def scoreAverager():
    done=0
    scoreTot=0
    numScores=0
    average=0
    while done == 0:
        score=input("Enter the numerical score, or enter 'x' to finish entering scores:")
        acceptedXs={"X","x"}
        if score in acceptedXs:
            print ("The average of the scores is: ",average)
            #break #this break is necessary for proper function.
            done=1
            testQuery()
        else:
            try:
                score=float(score)
                scoreTot=scoreTot+score
                numScores=numScores+1
                average=scoreTot/numScores
            except ValueError:
                print("EXCEPTION: The entry was invalid, please try again.")

def testQuery():
    done=0
    while done == 0:
        moreTests=input("Do you have a set of score to average? Enter 'Yes' or 'No':")
        acceptedNos=("No","NO","no") 
        acceptedYess=("Yes","YES","yes")
        if moreTests in acceptedNos:
            print("Program Complete.")
            done=1
        elif moreTests in acceptedYess:
            scoreAverager()
        else:
            print ("ERROR: The entry was invalid. Please try again.") 

def main():
    testQuery()

main()    

输入/输出示例:

Do you have a set of score to average? Enter 'Yes' or 'No':Yes
Enter the numerical score, or enter 'x' to finish entering scores:1
Enter the numerical score, or enter 'x' to finish entering scores:2
Enter the numerical score, or enter 'x' to finish entering scores:x
The average of the scores is:  1.5
Do you have a set of score to average? Enter 'Yes' or 'No':Yes
Enter the numerical score, or enter 'x' to finish entering scores:1
Enter the numerical score, or enter 'x' to finish entering scores:2
Enter the numerical score, or enter 'x' to finish entering scores:x
The average of the scores is:  1.5
Do you have a set of score to average? Enter 'Yes' or 'No':No
Program Complete.
Do you have a set of score to average? Enter 'Yes' or 'No':No
Program Complete.
Do you have a set of score to average? Enter 'Yes' or 'No':No
Program Complete.

【问题讨论】:

  • 您是否注意到循环在输入“否”后重新询问“您是否有要平均的分数”的次数取决于您之前说“是”并平均的次数数字?
  • 您再次从scoreAveranger() 内部调用testQuery()。您的程序应该只是传回调用 testQuery() 而不是再次启动另一个“嵌套”查询。当您在上面的示例中键入“否”时,您只会退出这些嵌套查询之一
  • 没有适合您的解决方案,但您可以通过更改为“if moretests.lower() == 'yes'”等来防止有人键入“yES”并生成错误消息等问题。祝你好运!
  • 你已经创建了间接递归!见下文。
  • 你真的应该避免在你的函数中到处打印,并专注于获取参数和返回值。此外,变量和函数名称应遵循lower_case_with_underscores 样式。

标签: python while-loop iteration off-by-one


【解决方案1】:

问题很复杂,解决方案很简单。通过数十次调试代码,我认识到问题出在scoreAverager 函数定义中的testQuery() 行。您在testQuery 函数仍在运行时进入循环,这会使程序在两个运行循环中done 的值变为1 时停止。

要解决这个问题,只需删除scoreAverager函数定义中的testQuery行,函数就会以与scoreAverager中的循环结束时相同的效率运行,它会返回到第一次启动循环进入testQuery

【讨论】:

  • 很好,感谢您的关注。我重新调用 testQuery 似乎是合理的,但我明白这如何以及为什么会导致进一步的问题。由于 scoreAverager 在 testQuery 中运行,因此让循环关闭而不是重新调用 testQuery 会更好也更容易。这也解决了休息的需要。
【解决方案2】:

我对您的代码进行了一些精简以隔离问题,嵌套了 ``scoreAvenger()``` 并使范围问题更加明确。看起来,您实际上在这里所做的是间接递归:您调用一个不直接调用自身的函数,而是调用另一个调用它的函数。因此,您创建了一个复杂的递归调用堆栈,它必须自行展开。

这可以使用nonlocal 修复,但前提是我们适当地嵌套函数;然后递归调用被终止[编辑:不!它们继续,但 done 的值没有设置为 0](因为嵌套函数捕获了 done 的值)。

def testQuery():
    def scoreAverager():
        nonlocal done       #here is the key: make done nonlocal 
        done = 0            #comment out above line to see the problem return
        while done == 0:
            score=input("Enter x")
            acceptedXs={"X","x"}
            if score in acceptedXs:
                print ("Returning to enclosing scope")
                done = True
                testQuery()
                print("stack unwinding in testQuery")


    done = 0
    while done == 0:
        moreTests=input("Want to enter nested scope/make another recursive call?")
        acceptedNos=("No","NO","no") 
        if moreTests in acceptedNos:
            print("Program Complete.")
            done = 1
        else:
            scoreAverager()
            print("stack unwinding in scoreAvenger")
def main():
    testQuery()

main()

这很棘手,我认为无论如何都是这样。 编辑:添加了在递归调用离开堆栈后执行的打印函数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-06-13
    • 2018-12-18
    • 1970-01-01
    • 1970-01-01
    • 2020-07-13
    • 1970-01-01
    • 2021-10-31
    • 2017-12-14
    相关资源
    最近更新 更多