Выход из середины цикла while без повторения кода

Я задаю пользователям ряд вопросов и записываю их ответы. Мои задающие вопросы проверяют форматирование этих ответов и возвращают флаг, если пользователь вводит что-то странное (например, my name is 102).

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

Обратите внимание, что в этом примере переменная letter является суррогатом пользовательского ввода. На самом деле код выглядит не так.

def string_checker(letter, output):
    if type(letter) != str:
        print('You did not give me a string!')
        output = 1

    return output

output = 0
# this should go until question 3, when the user makes a mistake
while output == 0:

    # question 1
    letter = 'bob'
    print(letter)
    output = string_checker(letter, output)

    # question 2
    letter = 'aldo'
    print(letter)
    output = string_checker(letter, output)

    # question 3 --- user gets this wrong
    letter = 1
    print(letter)
    output = string_checker(letter, output)

    # question 4
    letter = 'angry'
    print(letter)
    output = string_checker(letter, output)

# but it seems to ask question 4, regardless
print('done!')

Есть ли способ изменить этот код, чтобы question 4 никогда не запрашивался?

ОБНОВЛЕННЫЙ КОД НА ОСНОВЕ ОТВЕТА ДЖАСПЕРА

Основываясь на ответе Джаспера с полным решением ... эта модификация моей проблемы решила ее. Поднимая ValueError внутри функции проверки, блок try немедленно выходит из строя, и мы можем выйти из main, используя return.

def string_checker(letter):
    if type(letter) != str:
        raise ValueError

def main():
    # this should go until question 3, when the user makes a mistake
    try:

        # question 1
        letter = 'bob'
        print(letter)
        string_checker(letter)

        # question 2
        letter = 'aldo'
        print(letter)
        string_checker(letter)

        # question 3 --- user gets this wrong
        letter = 1
        print(letter)
        string_checker(letter)

        # question 4
        letter = 'angry'
        print(letter)
        string_checker(letter)

    # we make a mistake at question 3 and go straight to here
    except ValueError as ve:
        print('You did not give me a string!')
        return 'oops'

    # exit
    return 'done'

person jdv    schedule 22.07.2014    source источник
comment
Конечно, вам следует использовать цикл, который выполняет итерацию по списку букв, что-то вроде: for letter in ['x', 'y', 1, 'z']: print(letter); x = string_checker(letter, x); if x != 0: break (разброс по нескольким строкам, с правильным отступом и без точек с запятой). Если вы увидите, что почти один и тот же блок кода повторяется 4 раза таким образом, у вас должно возникнуть дрожь - или у вас подергивается нос от запаха кода. (Возможно, есть более эффективные способы кодирования этого, но это будет простой шаг вперед.)   -  person Jonathan Leffler    schedule 22.07.2014
comment
Да, мне тоже не нравится это повторение. Однако для того, чтобы у меня были все «буквы» в этом списке, мне пришлось бы уже задать набор вопросов, поэтому мой пользователь по-прежнему не жалеет потраченного впустую времени, если он допустит ошибку в процессе.   -  person jdv    schedule 22.07.2014
comment
Вы когда-нибудь собирались перебирать вопросы? Если нет, вы должны выбросить исключение, когда дается неправильный ответ.   -  person Jasper    schedule 22.07.2014
comment
Похоже, что циклы while вообще не делают то, что я хочу. Создание исключений работает не очень хорошо, так как оно прерывает поток вещей и заставляет пользователя начинать заново. Представьте, что это небольшой кусок из 100 вопросов, разделенных на маленькие модули, и если они сделают ошибку, я просто хочу, чтобы им пришлось повторить один модуль.   -  person jdv    schedule 22.07.2014
comment
Ваша функция, вероятно, должна выполнять print операцию. Я не вижу, как переход от отдельных букв к строкам сводит на нет мои наблюдения об использовании списка, но тогда я не вижу разницы между созданием списка (возможно, во время выполнения) и записью списка от руки (окончательно, когда вы кодируете ); действительно, использование списка открывает путь к гораздо большей гибкости. Действительно, неясно, откуда берутся струны; возможно, вы скрываете от нас какое-то взаимодействие с пользователем, что затрудняет оценку. Однако это ваша проблема, а не моя.   -  person Jonathan Leffler    schedule 22.07.2014
comment
Привет, Джонатан, да, я скрываю большое количество взаимодействий с пользователем, которое выходит далеко за рамки этого вопроса. Присваиваемая буквенная переменная является суррогатом пользовательского ввода. Если бы мой код действительно работал так, как я показал здесь, ваш список, несомненно, был бы лучшим. Я уточню это в своем вопросе.   -  person jdv    schedule 22.07.2014
comment
Пожалуйста, не вставляйте ответы в вопрос. Если у вас есть ответ, опубликуйте его как таковой (можно ответить на свой вопрос и даже принять ответ, если он хороший). Также старайтесь не добавлять в заголовок бесполезные теги. Не добавляйте просто случайный python в начале заголовка. Если вы уже поместили эту информацию в теги, этого достаточно. Заголовок должен быть правильно сформированным предложением без случайных тегов.   -  person Bakuriu    schedule 22.07.2014
comment
Зачем вам нужно проверять тип результата? Как вы просите пользователя ввести данные? AFAIK вы должны всегда получать строку от пользователя, тогда вы можете проверить, отформатирована ли строка определенным образом, или преобразовать ее в определенный тип. Если вы используете функцию input python2, имейте в виду, что вы не должны использовать ее, поскольку она открывает огромные дыры в безопасности в вашей программе. Также для проверки данного типа используйте isinstance(something, str), а не type(something) == str. Наконец, если длина кода зависит от количества вопросов, у вас все равно что-то не так.   -  person Bakuriu    schedule 22.07.2014
comment
Привет, Бакури, я прошу пользователя raw_input, преобразовывая его в соответствующий тип, а затем иногда проверяю целые числа или строки, или проверяю, является ли ввод int индексом в списке, или проверяю, является ли входная строка введите dict или ... Дело в том, что я хочу, чтобы код останавливался сразу после ошибки, давал обратную связь и начинался снова. Эти вопросы довольно специфичны для программ, к которым этот код Python получает доступ извне, поэтому длина кода должна масштабироваться с количеством задаваемых вопросов, но количество задаваемых вопросов жестко запрограммировано.   -  person jdv    schedule 22.07.2014
comment
Кроме того, я добавляю свой код решения обратно в свой ответ, потому что он не позволяет мне опубликовать ответ. Пожалуйста, не удаляйте его, я думаю, это поможет людям в будущем.   -  person jdv    schedule 22.07.2014


Ответы (3)


Хотя это не отвечает на заголовок вопроса, я считаю, что цикл - это неправильный выбор дизайна (при условии, что вы не собираетесь задавать одни и те же вопросы несколько раз). Вместо этого вы можете вызвать исключения:

try:
  # ask question

  if not string_checker(...):
    raise ValueError

  # ask next question

except ValueError as ve:
  print("wrong answer")
person Jasper    schedule 22.07.2014
comment
Спасибо за реализацию, работает отлично! Отредактирую свой ответ решением. - person jdv; 22.07.2014

Вы можете проверить, соответствует ли результат string_checker 1 после каждого вопроса:

if output == 1:
    break

Оператор break немедленно выйдет из цикла.

Таким образом, вам не нужно будет иметь это условие в while, поэтому вы можете делать бесконечное while:

while True:
    ...
person Christian    schedule 22.07.2014
comment
Это достойное решение, но только усугубляет опасения Джонатана Леффлера (и меня самого) по поводу большого количества повторяющегося кода. - person jdv; 22.07.2014

# question 3 --- user gets this wrong
letter = 1
print(letter)
output = string_checker(letter, output)

# Add this to your code:

if output == 1:
    break

Разрыв полностью выпадает из цикла while, и все готово. Единственная проблема в том, что компьютер все равно будет

print('done!')

Так что, возможно, вы захотите включить код ошибки или что-то подобное.

Может что-то вроде?

if output == 1:
    print "You have given an invalid input"
    break

РЕДАКТИРОВАТЬ:

Я понял, что он напечатает «Вы ввели неверный ввод», а затем напечатает «Готово!»

Поэтому вместо этого вы должны остановить выполнение программы с недопустимым вводом:

if output == 1:
    print "You have given an invalid input"
    return

Оператор return полностью остановит программу.

person Mburdzy    schedule 22.07.2014
comment
Лол расслабься. Я не видел его ответа, когда набирал свой. - person Mburdzy; 22.07.2014
comment
Без проблем. вы должны удалить свой ответ, так как это копия, и некоторые люди проголосуют за нее как таковую :) - person John Ruddell; 22.07.2014