Як зробити “подвійний break”, тобто вийти зі вкладеного циклу, в Python?


Дізнайтесь більше про нові кар'єрні можливості в EchoUA. Цікаві проекти, ринкова оплата, гарний колектив. Надсилайте резюме та приєднуйтеся до нас.

Умова:

Перебрати всі пари символів у рядку, і зупинитися при знаходженні двох однакових символів.

 

Розв’язання очевидне, але постає запитання:

s = "якийсь рядок" for i in range (len (s)): for j in range (i+1, len (s)): if s[i] == s[j]: print (i, j) break # Як вийти відразу з двох циклів?

Якби ми програмували, наприклад, на Java, то ми могли б скористатися механізмом міток:

outterLoop: for (int i=0; i<n; i++){ for (int j=i; j<n; j++){ if (/*something*/){ break outterLoop; } }}

Проте в Python такого механізму немає. Вимагається запропонувати найбільш зручне у використанні й читане розв’язання.

Можливі варіанти відповіді

  • Помістити цикл в тіло функції, а потім зробити return з неї:
    def func (): s=" teste" for i in range (len (s)): for j in range (i+1, len (s)): if s[i]==s[j]: print (i, j) returnfunc ()

    Чому це погана ідея? Зрозуміло, саме завдання в умові – лише абстрактний приклад. У житті циклів може бути значно більше, і створювати по функції для кожного з них якось неприродно, чи не так?

  • Вивести виняток і піймати його зовні циклу:
    try: s=" teste" for i in range (len (s)): for j in range (i+1, len (s)): if s[i]==s[j]: print (i, j) raise Exception ()except: print ("the end")

    Чому це погана ідея? Тут ми використовуємо механізм винятків як особливу форму goto, але ж насправді нічого виняткового в коді не сталося – це звичайна ситуація. Як мінімум, причини такого зловживання цим механізмом будуть незрозумілі іншим програмістам.

  • Можна створити булеву змінну, яка зберігатиме інформацію про те, чи треба виходити із зовнішнього циклу на цій ітерації:
    exitFlag=Falses=" teste" for i in range (len (s)): for j in range (i+1, len (s)): if s[i]==s[j]: print (i, j) exitFlag=True break if (exitFlag): break

    Чому це погана ідея? З усіх перелічених вище ідей ця, мабуть, краща. Проте, це дуже низькорівневий підхід, і в мові Python є можливість реалізувати задумане якнайкраще.

  • Використати замість двох циклів for один while:
    s=" teste" i=0j=1while i < len (s): if s[i] == s[j]: print (i, j) break j=j+1 i=i+j//len (s) j=j%len (s)

    Чому це погана ідея? Вам не здається, що такий код читається гірше за всі запропоновані варіанти?

Розв’язання на “відмінно”

Давайте ще раз уважно прочитаємо умову:

Перебрати всі пари символів у рядку, і зупинитися при знаходженні двох однакових символів.

Є там взагалі хоч слово про подвійний цикл або про перебір двох індексів? Нам треба перебирати пари. Отже, вірогідно, ми повинні написати щось подібне до цього:

s = " teste" for i, j in unique_pairs (len (s)): if s[i] == s[j]: print (i, j)
break

Відмінно, так ми перебиратимемо пари. Як нам добитися саме такої форми запису? Все дуже просто, треба створити генератор. Робиться це таким чином:

def unique_pairs (n): for i in range (n): for j in range (i+1, n): yield i, j

“Як це працює?” – запитаєте Ви. Все просто. При виклику unique_pairs (int) код у тілі функції не обчислюється. Замість цього буде повернений об’єкт генератора. Кожен виклик методу next () цього генератора (що неявно відбувається при кожній ітерації циклу for) код у його тілі виконуватиметься до тих пір, поки не буде зустрінуто ключове слово yield. Після чого виконання буде призупинено, а метод поверне вказаний об’єкт (тут yield діє подібно return). За наступного виклику функція почне виконуватися не з початку, а з того місця, на якому зупинилася попереднього разу. При закінченні перебору буде виведено виняток StopIteration.

Отже, самий true pythonic way у розв’язанні цієї задачі:

def unique_pairs (n): for i in range (n) :
for j in range (i+1, n): yield i, js = "a string to examine" for i, j in unique_pairs (len (s)): if s[i] == s[j]: print (i, j) break

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

itertools.combinations (s, 2)

Що ж, для цієї задачі це дійсно більше pythonic-розв’язання. Зазначимо, що метою статті було швидше ознайомити новачків із механізмом генераторів, ніж розв’язати проблему, заявлену в першому абзаці.


Свої варіанти пропонуйте в коментарях!

Розбір узятий зі статті “Breaking out of two loops”

Київ, Харків, Одеса, Дніпро, Запоріжжя, Кривий Ріг, Вінниця, Херсон, Черкаси, Житомир, Хмельницький, Чернівці, Рівне, Івано-Франківськ, Кременчук, Тернопіль, Луцьк, Ужгород, Кам'янець-Подільський, Стрий - за статистикою саме з цих міст програмісти найбільше переїжджають працювати до Львова. А Ви розглядаєте relocate?


Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *