Python - wprowadzenie

07 - Pętle

Czym są pętle?

Pętle pozwalają na powtarzanie fragmentów kodu. Zacznijmy od prostego przykładu:

i = 1
while i <= 10:
    print(i)
    i = i + 1
print("Koniec")
1
2
3
4
5
6
7
8
9
10
Koniec

Jak widać konstrukcja while jest nieco podobna do if. Po słowie kluczowym while następuje test, jeśli zwraca True, wykonywany jest ,,wcięty’’ blok kodu. O ile jednak w przypadku konstrukcji if wykonywany jest jednokrotnie, w przypadku pętli po wykonaniu bloku kodu ponownie sprawdzany jest warunek i jeśli dalej zwraca True, blok kodu wykonuje się kolejny raz. Wszystko powtarza się do momentu aż zostanie zwrócone False, wtedy wykonanie programu przechodzi do części poza pętlą.

Przyjrzyjmy się jak powyższy kod działa:

# przypisanie do zmiennej i wartości 1
i = 1 
# sprawdzenie, czy wartość przypisana 
# zmiennej i jest mniejsza lub równa 10
while i <= 10: 
# Jeśli i jest <= 10 (test zwraca True)
# to wykonywany jest poniższy, "wcięty" 
# blok kodu
    print(i)
    i = i + 1
# Kod wykonywanu po wyjściu z pętli
print("Koniec")

Zauważ, że zmienna i służy w powyższy kodzie jako ,,licznik’’. Możemy jej użyć do sprawdzenia i/lub wydrukowania numeru przebiegu pętli albo np. do obliczeń czy w innych celach.

Powtarzanie czynności, lub zestawu instrukcji wykonywanych w pętli nazywamy iteracją, termin też odnosi się do pojedynczego wykonania kodu w pętli.

Przy okazji warto poznać skrócone operatory arytmetyczne, które służą jak sama nazwa wskazuje do krótszego zapisu pewnych operacji arytmetycznych. Ich znaczenia znajdują się poniższej tabelce:

wyrażenie znaczenie
x += y x = x + y
x -= y x = x – y
x *= y x = x * y
x /= y x = x / y

Są one przydatne do zmiany wartości zmiennej.

Możemy zatem zmodyfikować powyższy przykład:

i = 1
while i <= 10:
    print(i)
    i += 1
print("Koniec")
1
2
3
4
5
6
7
8
9
10
Koniec

Konstuując pętle, pamiętajmy o tym, żeby możliwe było wyjście z niej. Usuń z powyższego kodu linię i += 1, teraz kod będzie wyglądał tak:

i = 1
while i <= 10:
    print(i)
print("Koniec")

Po uruchomieniu, program będzie wykonywał się bez końca, w tym przypadku określenie ,,zapętlił się’’ można rozumieć dosłownie. W JupyterLab można przerwać wykonanie kodu w komórce (cell) klikając w ikonę z kwadratem (Interrupt the kernel)

lub wybierając komendę Interrupt the kernel z panelu komend (który można otworzyć skrótem Ctrl+Shift+C).

Po zatrzymaniu pętli poniżej pojawi nam się komórka z wynikiem, zawierająca niezliczoną liczbę jedynek. Przydałoby się ją wyczyścić.

W tym celu najedź kursorem na niebieski pasek po lewej stronie komórki z jedynkami:

Następnie kliknij prawym przyciskiem myszy na niebieskim pasku, pojawi się menu. Wybierz Clear Outputs:

Innym rodzajem błędu, który można popełnić, jest nieprawidłowe sformułowanie testu, np:

i = 1
while i >= 10:
    print(i)
    i += 1
print("Koniec")

Pomyłka polegająca na wstawieniu znaku > zamiast < skutkuje tym, że pętla nie wykona się ani razu.

Z kolei taki warunek znów doprowadzi do ,,zapętlenia’':

i = 10
while i >= 0:
    print(i)
    i += 1
print("Koniec")

Przypuśćmy, że autorowi pętli chodziło o wypisanie liczb od 10 do 0. Jak poprawić pętlę aby działała poprawnie?

i = 10
while i >= 0:
    print(i)
    i -= 1
print("Koniec")
10
9
8
7
6
5
4
3
2
1
0
Koniec

Teraz spróbujmy narysować taką ,,choinkę’':

         *         
        ***        
       *****       
      *******      
     *********     
    ***********    
   *************   
  ***************  
 ***************** 
*******************

Bez znajomości pętli pewnie napisalibyśmy coś w tym rodzaju:

print("         *")     
print("        ***")         
print("       *****")        
print("      *******")
...
         *
        ***
       *****
      *******

… itd. Jeśli użyjemy f-strings, można by to zapisać tak:

print(f"{'*': ^19}")
print(f"{'***': ^19}")
print(f"{'*****': ^19}")
print(f"{'*******': ^19}")
...
         *         
        ***        
       *****       
      *******      

.. itd.

Program oczywiście narysuje to co chcemy, ale będzie niepotrzebnie długi - na każdą linię ,,choinki’’ będzie przypadać jedna linia kodu. Zastanów się jak można wykorzystać pętlę, aby wykonać zadanie.

Można to zrobić np. tak:

i = 1
while i <= 19:
    print(f"{i*'*': ^19}")
    i += 2
         *         
        ***        
       *****       
      *******      
     *********     
    ***********    
   *************   
  ***************  
 ***************** 
*******************

Program jest teraz krótszy, łatwiej modyfikowalny. Spróbuj teraz go zmodyfikować tak, aby uzyskać ,,pień’':

          *          
         *|*         
        **|**        
       ***|***       
      ****|****      
     *****|*****     
    ******|******    
   *******|*******   
  ********|********  
 *********|********* 
**********|**********
i = 1
print(f"{i*'*': ^21}")
while i <= 10:
    print(f"{i*'*': >10}|{i*'*': <10}")
    i += 1
          *          
         *|*         
        **|**        
       ***|***       
      ****|****      
     *****|*****     
    ******|******    
   *******|*******   
  ********|********  
 *********|********* 
**********|**********

,,Znowu mam to powtarzać?''

Powyższe pętle były wykonywane określoną, z góry określoną liczbę razy. Nie zawsze jednak tego właśnie chcemy. Czasem wyjście z pętli powinno nastąpić po określonym wydarzeniu, np. podaniu przez użytkownika odpowiedniego polecenia, czy też uzyskaniu przez program określonej wartości.

Wróćmy na chwilę do programu, który pisaliśmy przy okazji poznawania f-string, obliczającego stężenie rozpuszczonej substancji.

m_roztworu = 13
m_substancji_rozpuszczonej = 7.3
print(f"""
masa roztworu:                 {m_roztworu:>5.2f}g
masa substancji rozpuszczonej: {m_substancji_rozpuszczonej:>5.2f}g
stężenie:                      {100*m_substancji_rozpuszczonej/m_roztworu:>5.2f}%
""")
masa roztworu:                 13.00g
masa substancji rozpuszczonej:  7.30g
stężenie:                      56.15%

Spróbujmy teraz nieco rozbudować program. Po pierwsze, niech dane wejściowe będą podawane przez użytkownika, po drugie, niech program działa obliczając kolejne stężenia aż użytkownik nie zechce go zakończyć.

Zastanów się najpierw, jak zrealizować pierwsze zadanie.

Można to zrobić np. tak:

# Pobranie danych od użytkownika
m_roztworu = float(input('Podaj masę roztworu [g]: '))
m_substancji_rozpuszczonej = float(input('Podaj masę substancji rozpuszczonej [g]: '))
# Obliczenia
print(f"""
masa roztworu:                 {m_roztworu:>5.2f}g
masa substancji rozpuszczonej: {m_substancji_rozpuszczonej:>5.2f}g
stężenie:                      {100*m_substancji_rozpuszczonej/m_roztworu:>5.2f}%
""")
Podaj masę roztworu [g]:  300
Podaj masę substancji rozpuszczonej [g]:  50

masa roztworu:                 300.00g
masa substancji rozpuszczonej: 50.00g
stężenie:                      16.67%

Jak teraz zmodyfikować program tak, aby nie kończył działania po obliczeniu wyników, ale pytał użytkownika, czy chce kontynuować i w zależności od decyzji powtarzał działania lub kończył działanie programu?

Oczywiście użyjemy pętli:

# Zmienna przechowująca informację, czy kontynuować działanie pętli
kontynuowac = True
# Pętla, działa dopóki zmienna kontynuować na wartość True
while kontynuowac:
    # Pobranie danych od użytkownika
    m_roztworu = float(input('Podaj masę roztworu [g]: '))
    m_substancji_rozpuszczonej = float(input('Podaj masę substancji rozpuszczonej [g]: '))
    # Obliczenia
    print(f"""
    masa roztworu:                 {m_roztworu:>5.2f}g
    masa substancji rozpuszczonej: {m_substancji_rozpuszczonej:>5.2f}g
    stężenie:                      {100*m_substancji_rozpuszczonej/m_roztworu:>5.2f}%
    """)
    # Po wykonaniu iteracji pytamy użytkownika, czy chce kontynuować
    decyzja = input("Czy chcesz kontynować? [T/N]")
    # Jeśli nie chce...
    if decyzja.upper() == 'N':
        kontynuowac = False
    # Jeśli chce
    else:
        print("\n______________________\n")

print("Koniec")
Podaj masę roztworu [g]:  21
Podaj masę substancji rozpuszczonej [g]:  2

    masa roztworu:                 21.00g
    masa substancji rozpuszczonej:  2.00g
    stężenie:                       9.52%

Czy chcesz kontynować? [T/N] t

______________________

Podaj masę roztworu [g]:  100
Podaj masę substancji rozpuszczonej [g]:  10


    masa roztworu:                 100.00g
    masa substancji rozpuszczonej: 10.00g
    stężenie:                      10.00%

Czy chcesz kontynować? [T/N] n

Koniec

Zauważ, że program kontynuuje działanie, jeśli użytkownik podał jakąkolwiek odpowiedź oprócz n lub N. Spróbuj zmodyfikować program tak, aby program pytał o decyzję tak długo, aż użytkownik wpisze T, t, N lub n.

# Zmienna przechowująca informację, czy kontynuować działanie pętli
kontynuowac = True
# Pętla, działa dopóki zmienna kontyuować na wartość True
while kontynuowac:
    # Pobranie danych od użytkownika
    m_roztworu = float(input('Podaj masę roztworu [g]: '))
    m_substancji_rozpuszczonej = float(input('Podaj masę substancji rozpuszczonej [g]: '))
    # Obliczenia
    print(f"""
    masa roztworu:                 {m_roztworu:>5.2f}g
    masa substancji rozpuszczonej: {m_substancji_rozpuszczonej:>5.2f}g
    stężenie:                      {100*m_substancji_rozpuszczonej/m_roztworu:>5.2f}%
    """)
    # Po wykonaniu iteracji pytamy użytkownika, czy chce kontynuować
    # Pętla działająca dopóki użytkownik nie wprowadzi T, t, N lub n
    # Początkowa wartość zmiennej decyzja gwarantująca uruchomienie pętli
    # po raz pierwszy
    decyzja = 'X'
    while decyzja.upper() != 'N' and decyzja.upper() != 'T': 
        decyzja = input("Czy chcesz kontynować? [T/N]")       
        # Jeśli nie chce...
        if decyzja.upper() == 'N':
            kontynuowac = False
        # Jeśli chce
        elif decyzja.upper() == 'T':
            print("\n______________________\n")
        else:
            print('Nieprawidłowa odpowiedź, wprowadź T, t, N lub n')

print("Koniec")
Podaj masę roztworu [g]:  12
Podaj masę substancji rozpuszczonej [g]:  2

    masa roztworu:                 12.00g
    masa substancji rozpuszczonej:  2.00g
    stężenie:                      16.67%

Czy chcesz kontynować? [T/N] W

Nieprawidłowa odpowiedź, wprowadź T, t, N lub n

Czy chcesz kontynować? [T/N] n

Koniec

W celu przerwania pętli można także używać komendy break:

# Zmienna przechowująca informację, czy kontynuować działanie pętli
kontynuowac = True
# Pętla, działa dopóki zmienna kontyuować na wartość True
while kontynuowac:
    # Pobranie danych od użytkownika
    m_roztworu = float(input('Podaj masę roztworu [g]: '))
    m_substancji_rozpuszczonej = float(input('Podaj masę substancji rozpuszczonej [g]: '))
    # Obliczenia
    print(f"""
    masa roztworu:                 {m_roztworu:>5.2f}g
    masa substancji rozpuszczonej: {m_substancji_rozpuszczonej:>5.2f}g
    stężenie:                      {100*m_substancji_rozpuszczonej/m_roztworu:>5.2f}%
    """)
    # Po wykonaniu iteracji pytamy użytkownika, czy chce kontynuować
    # Pętla działająca dopóki użytkownik nie wprowadzi T, t, N lub n
    while True: 
        decyzja = input("Czy chcesz kontynować? [T/N]")       
        # Jeśli nie chce...
        if decyzja.upper() == 'N':
            kontynuowac = False
            break
        # Jeśli chce
        elif decyzja.upper() == 'T':
            print("\n______________________\n")
            break
        else:
            print('Nieprawidłowa odpowiedź, wprowadź T, t, N lub n')

print("Koniec")
Podaj masę roztworu [g]:  12
Podaj masę substancji rozpuszczonej [g]:  3

    masa roztworu:                 12.00g
    masa substancji rozpuszczonej:  3.00g
    stężenie:                      25.00%

Czy chcesz kontynować? [T/N] x

Nieprawidłowa odpowiedź, wprowadź T, t, N lub n

Czy chcesz kontynować? [T/N] t

______________________

Podaj masę roztworu [g]:  12
Podaj masę substancji rozpuszczonej [g]:  10

    masa roztworu:                 12.00g
    masa substancji rozpuszczonej: 10.00g
    stężenie:                      83.33%

Czy chcesz kontynować? [T/N] n

Koniec

,,Idźmy dalej''

Spróbujmy jeszcze nieco poprawić nasz program, tak żeby w przypadku wpisania liczby mniejszej od 0 przerywał wykonywanie głównej pętli i wracał do początku. Nie ma przecież sensu liczyć stężenia dla ujemnej masy roztworu, czy substancji rozpuszczonej. W przypadku podania błędnych danych nie ma też sensu wykonywać dalszej części kodu. Prostym rozwiązaniem jest przejść od razu do początku pętli aby umożliwić użytkownikowi podanie właściwych wartości. Przyda nam się tu polecenie continue, które przekierowuje wykonanie kodu do początku pętli.

# Zmienna przechowująca informację, czy kontynuować działanie pętli
kontynuowac = True
# Pętla, działa dopóki zmienna kontyuować na wartość True
while kontynuowac:
    # Pobranie danych od użytkownika
    m_roztworu = float(input('Podaj masę roztworu [g]: '))
    if m_roztworu < 0:
        continue
    m_substancji_rozpuszczonej = float(input('Podaj masę substancji rozpuszczonej [g]: '))
    if m_substancji_rozpuszczonej < 0:
        continue
    # Obliczenia
    print(f"""
    masa roztworu:                 {m_roztworu:>5.2f}g
    masa substancji rozpuszczonej: {m_substancji_rozpuszczonej:>5.2f}g
    stężenie:                      {100*m_substancji_rozpuszczonej/m_roztworu:>5.2f}%
    """)
    # Po wykonaniu iteracji pytamy użytkownika, czy chce kontynuować
    # Pętla działająca dopóki użytkownik nie wprowadzi T, t, N lub n
    while True: 
        decyzja = input("Czy chcesz kontynować? [T/N]")       
        # Jeśli nie chce...
        if decyzja.upper() == 'N':
            kontynuowac = False
            break
        # Jeśli chce
        elif decyzja.upper() == 'T':
            print("\n______________________\n")
            break
        else:
            print('Nieprawidłowa odpowiedź, wprowadź T, t, N lub n')

print("Koniec")
Podaj masę roztworu [g]:  10
Podaj masę substancji rozpuszczonej [g]:  5

    masa roztworu:                 10.00g
    masa substancji rozpuszczonej:  5.00g
    stężenie:                      50.00%

Czy chcesz kontynować? [T/N] t
______________________

Podaj masę roztworu [g]:  -12
Podaj masę roztworu [g]:  10
Podaj masę substancji rozpuszczonej [g]:  2

    masa roztworu:                 10.00g
    masa substancji rozpuszczonej:  2.00g
    stężenie:                      20.00%

Czy chcesz kontynować? [T/N] t
______________________

Podaj masę roztworu [g]:  20
Podaj masę substancji rozpuszczonej [g]:  -5
Podaj masę roztworu [g]:  20
Podaj masę substancji rozpuszczonej [g]:  5

    masa roztworu:                 20.00g
    masa substancji rozpuszczonej:  5.00g
    stężenie:                      25.00%

Czy chcesz kontynować? [T/N] n

Koniec

,,Sprawdźmy to litera po literze"

Napiszmy teraz krótki kod, który wypisze nam kolejne litery z podanego ciągu znaków, np. zasady z sekwencji DNA/RNA. Zastanów się najpierw jak można by to zrobić bazując na zdobytych dotychczas umiejętnościach.

Na przykład tak:

sekwencja = input('podaj sekwencję')
dlugosc = len(sekwencja)
i = 0
while i < dlugosc:
    print(sekwencja[i])
    i += 1
podaj sekwencję GACCTAT

G
A
C
C
T
A
T

Powyższy kod zadziała, ale Python oferuje bardziej eleganckie rozwiązanie przy użyciu pętli z użyciem for:

sekwencja = input('Podaj sekwencję: ')
for litera in sekwencja:
    print(litera)
Podaj sekwencję:  GATTACA

G
A
T
T
A
C
A

Rozwiń teraz powyższy kod tak, aby tłumaczył znaczenie każdej z liter oznaczających zasadę azotową. Można tu zastosować nieco zmodyfikowany kod z lekcji poświęconej instrukcjom wyboru:

# PObranie sekwencji od użytkownika
sekwencja = input('Podaj sekwencję: ')
for litera in sekwencja:
    # Sprawdzenie znaczenia poszczególnych liter
    if litera.upper() == 'A':
        print(f'{litera} - adenina')
    elif litera.upper() == 'C':
        print(f'{litera} - cytozyna')
    elif litera.upper() == 'G':
        print(f'{litera} - guanina')
    elif litera.upper() == 'T':
        print(f'{litera} - tymina')
    elif litera.upper() == 'U':
        print(f'{litera} - uracyl')
    else:
        print(f'{litera} - błędna litera')
Podaj sekwencję:  GATTACA

G - guanina
A - adenina
T - tymina
T - tymina
A - adenina
C - cytozyna
A - adenina

,,Po kolei numerami…"

Powyżej napisaliśmy pętle, która wypisywała kolejne liczby od 1 do 10. Można w tym celu także użyć for w połączeniu z poleceniem range():

for i in range(1,11):
    print(i)
1
2
3
4
5
6
7
8
9
10

Polecenie range() generuje ciąg liczb. Zauważ, że przekazaliśmy poleceniu range() dwie liczby: pierwsza (1) to początek serii generowanych liczb, druga (11) to jej koniec, ale, co bardzo ważne, generowany ciąg liczb nie zawiera tej drugiej wartości, ale wartość o 1 mniejszą. Można też podać trzecią liczbę, która będzie oznaczała wielkość ,,kroku’’, domyślnie jest to 1:

for i in range(1, 11, 2):
    print(i)
1
3
5
7
9

Jak widać, teraz została wygenerowana co druga liczba (krok = 2).

Gdybyśmy chcieli uzyskać liczby od 10 do 1 (czyli w odwrotnej kolejności), należałoby to zrobić tak:

for i in range(10, 0, -1):
    print(i)
10
9
8
7
6
5
4
3
2
1

Zauważ, że teraz podaliśmy jako pierwszą liczbę 10 a jako drugą 0. Także w tym przypadku pierwsza jest włączona w generowaną serię liczb a druga nie. W tym przypadku nie możemy pominąć trzeciej liczby, jest to liczba ujemna, ponieważ ,,kroczymy wstecz".

Funkcja range(), kiedy podamy jedną liczbę x jako argument, generuje liczby od 0 do x-1.

for i in range(5):
    print(i)
0
1
2
3
4

Zadanie

Napisz program, który pobierze od użytkownika długi ciąg znaków, oraz krótki ciąg znaków, a następnie wypisze wszystkie miejsca wystąpienia w intuicyjnie zrozumiały sposób (czyli pierwszy znak znajduje się w miejscu 1 a nie 0) krótkiego ciągu znaków, a następnie całkowitą liczbę wystąpień.

Uwaga: jeśli np. długi ciąg to ‘AAAAAA’, a szukamy ciągu ‘AA’ to program powinien podać wszystkie wystąpienia ciągu ‘AA’, nawet jeśli kolejne wystąpienie znajduje się w obrębie poprzedniego znalezionego ciągu. Czyli powinniśmy otrzymać położenia: 1, 2, 3, 4, 5.

Przykładowa sesja:

Podaj krótki ciąg znaków:  AAACTAAGGAAA
Podaj krótki ciąg znaków:  AA
__________________________________________________
Miejsca wystąpień ciągu "AA": 1, 2, 6, 10, 11, 
Liczba wszystkich wystąpień ciągu "AA": 5

Przykładowe rozwiąznie

dlugi = input('Podaj krótki ciąg znaków: ')
krotki = input ('Podaj krótki ciąg znaków: ')
liczba_wystapien = 0
# Ciąg znaków, który będzie zwierał miejsca wystąpien
miejsca = ''
# Zmienna, która kontroluje wykonanie pętli
kontynuacja = True
# Miejsce rozpoczęcia wyszukiwania -1
miejsce = -1
# Petla sprawdzająca występowanie ciągu
while kontynuacja:
    # Wyszukanie ciągu, od początku lub od miejsca 
    # ostatniego znalezionego ciagu
    miejsce = dlugi.find(krotki, miejsce+1)
    # Znaleziono ciąg
    if miejsce >=0:
        # Dodawanie kolejnego wyniku do ciągu znaków
        # z miejscami wystąpień
        miejsca = miejsca+str(miejsce+1)+', '
        liczba_wystapien += 1
    else:
        # Jeśli nie znaleziono to trzeba przerwać
        # wykonywanie pętli
        kontynuacja = False
# Wydruk wyników
print('_'*50)
print(f'Miejsca wystąpień ciągu "{krotki}": {miejsca}')        
print(f'Liczba wszystkich wystąpień ciągu "{krotki}": {liczba_wystapien}')
Last updated on 5 Nov 2020
Published on 5 Nov 2020