Как сделать «двойной break», то есть выйти из вложенного цикла, в Python?

Posted by

Условие:

Решение достаточно очевидное, но возникает вопрос:

Если бы мы программировали, например, на Java, то мы могли бы воспользоваться механизмом меток:

Однако в Python такого механизма нет. Требуется предложить наиболее удобное в использовании и читаемое решение.

Возможные варианты ответа

— Поместить цикл в тело функции, а затем сделать return из неё:def func():ts=»teste»tfor i in range(len(s)):ttfor j in range(i+1, len(s)):tttif s[i]==s[j]:ttttprint(i,j)ttttreturnfunc()Почему это плохая идея: разумеется, сама задача в условии — лишь абстрактный пример. В жизни циклов может быть гораздо больше, и создавать по функции для каждого из них как-то неестественно, не так ли?
— Выбросить исключение и поймать его снаружи цикла:try:ts=»teste»tfor i in range(len(s)):ttfor j in range(i+1, len(s)):tttif s[i]==s[j]:ttttprint(i,j)ttttraise Exception()except:tprint(«the end»)Почему это плохая идея: здесь мы используем механизм исключений как особую форму goto, но ведь на самом деле ничего исключительного в коде не произошло — это обычная ситуация. Как минимум, причины такого злоупотребления этим механизмом будут непонятны другим программистам.
— Можно создать булевую переменную, которая будет хранить информацию о том, нужно ли выходить из внешнего цикла на данной итерации:exitFlag=Falses=»teste»for i in range(len(s)):tfor j in range(i+1, len(s)):ttif s[i]==s[j]:tttprint(i,j)tttexitFlag=Truetttbreaktif(exitFlag):ttbreakПочему это плохая идея: из всех перечисленных выше идей эта, пожалуй, лучшая. Тем не менее, это весьма низкоуровневый подход, и в языке Python есть возможность реализовать задуманное гораздо лучше.
— Использовать вместо двух циклов for один while:s=»teste»i=0j=1while i < len(s):tif s[i] == s[j]:ttprint(i, j)ttbreaktj=j+1ti=i+j//len(s)tj=j%len(s)Почему это плохая идея: вам не кажется, что такой код читается хуже всех предложенных вариантов?

Решение на пятёрку

Давайте ещё раз внимательно прочитаем условие:

Где там вообще хоть слово про двойной цикл или про перебор двух индексов? Нам нужно перебирать пары. Значит, по идее, мы должны написать что-то вроде этого:

Отлично, так мы будем перебирать пары. Но как нам добиться именно такой формы записи? Всё очень просто, нужно создать генератор. Делается это следующим образом:

“Как это работает?” — спросите вы. Всё просто. При вызове unique_pairs(int) код в теле функции не вычисляется. Вместо этого будет возвращён объект генератора. Каждый вызов метода next() этого генератора (что неявно происходит при каждой итерации цикла for) код в его теле будет выполняться до тех пор, пока не будет встречено ключевое слово yield. После чего выполнение будет приостановлено, а метод вернёт указанный объект (здесь yield действует подобно return). При следующем вызове функция начнёт выполняться не с начала, а с того места, на котором остановилась в прошлый раз. При окончании перебора будет выброшено исключение StopIteration.

Итак, самый true pythonic way в решении этой задачи:

UPD: в комментариях подсказывают, что такой генератор уже реализован в стандартной библиотеке:

Что же, для данной задачи это действительно более pythonic решение. Хочется отметить, что целью статьи было скорее познакомить новичков с механизмом генераторов, нежели действительно решить проблему, заявленную в первом абзаце ?

Свои варианты предлагайте в комментариях!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *