Python l oop повторяется после ожидаемого завершения - PullRequest
2 голосов
/ 20 февраля 2020

Код ниже Python имеет странное поведение, которое я не могу понять.

Программа вызывает testQuery, который запрашивает используемый ответ «да» для вызова ScoreAverager или «нет» выйти из программы. Если вызывается ScoreAverager, он затем запрашивает у пользователя ввести серию баллов или 'x' в fini sh, что возвращает пользователя в testQuery, где они могут выбрать усреднение другого теста или выхода.

Странность возникает, если пользователь усредняет результаты для нескольких тестов (например, 2 или более). В этот момент отказ от testQuery не завершит работу программы. Он будет повторять дополнительный цикл для каждого усредненного теста. Я не могу понять, почему это так. Он выглядит и ведет себя как ошибка, но условия для закрытия l oop выглядят удовлетворенными. Это можно решить с помощью «перерыва», но я бы лучше знал, в чем заключается проблема, чтобы решить его более органично.

Может кто-нибудь дать мне знать, почему происходит такое странное поведение?

КОД:

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.

Ответы [ 2 ]

0 голосов
/ 20 февраля 2020

Я немного урезал ваш код, чтобы изолировать проблему, вложил `` 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()

Это довольно сложно, я думаю, что это все равно происходит. Редактировать: добавлены функции печати, которые выполняются после того, как рекурсивные вызовы выходят из стека.

0 голосов
/ 20 февраля 2020

Проблема довольно сложная, где решение довольно простое. Отладив код десятки раз, я понял, что проблема в вашей строке testQuery() в определении функции scoreAverager. Вы вводите функцию l oop внутри testQuery, пока она еще работает, и это останавливает программу, когда значение done становится равным 1 в двух рабочих циклах.

Чтобы решить эту проблему, просто удалите строку testQuery в определении функции scoreAverager, и функция будет работать с той же эффективностью, что и при завершении l oop в scoreAverager, она будет вернуться к первому запущенному l oop in testQuery.

...