TI/Pętle

Z Brain-wiki
Wersja z dnia 14:38, 28 cze 2016 autorstwa RobertJB (dyskusja | edycje) (→‎Przykład)
(różn.) ← poprzednia wersja | przejdź do aktualnej wersji (różn.) | następna wersja → (różn.)

Pętla while

Wyrażenie

while warunek: 
    blok

służy do konstrukcji bloku poleceń pętli wykonywanej warunkowo. Jak w przypadku każdego bloku w Pythonie, do bloku po while zaliczają się te linijki, które są wyrównane do tej samej kolumny (mają takie samo wcięcie). Python najpierw sprawdza czy warunek jest spełniony, i jeśli tak, to wykonuje wszystkie wyrażenia zawarte w bloku. Następnie ponownie sprawdza warunek, i jeśli nadal jest spełniony, to ponownie wykonuje wszystkie wyrażenia zawarte w bloku. Pętla jest wykonywana tak długo, jak długo warunek jest prawdziwy.

Przykład

#!/usr/bin/python
# -*- coding: utf-8 -*-

liczba = 23
dzialaj = True
while dzialaj:
    strzal = int(raw_input('Wpisz liczbę całkowitą: '))
    if strzal == liczba:
        print 'Gratulacje, zgadłeś ją!'
        print '(Ale nic nie wygrałeś.)'
        dzialaj = False  # To sprawia, że warunek przestaje być
	                 # spełniony
    elif strzal < liczba:
        print 'Nie, szukana liczba jest większa od podanej.'
    else:
        print 'Nie, szukana liczba jest mniejsza od podanej.'
print 'Koniec programu.'
Rezultat:
$ python while.py
Wpisz liczbę całkowitą: 50
Nie, ta liczba jest mniejsza od szukanej. 
Wpisz liczbę całkowitą: 22
Nie, ta liczba jest większa od szukanej.
Wpisz liczbę całkowitą: 23
Gratulacje, zgadłeś ją!
(Ale nic nie wygrałeś.)

Koniec programu.
Jak to działa?

W tym programie gramy w zgadywankę, dopóki użytkownik nie poda prawidłowej odpowiedzi.

Nie ma konieczności ponownego uruchamiania programu po każdym strzale (por. [[../Wykonanie warunkowe|poprzedni rodział]]). Przesunęliśmy wyrażenia raw_input oraz if do wnętrza pętli while i ustawiliśmy zmienną dzialaj na wartość True przed pętlą while. Najpierw sprawdzamy wartość zmiennej dzialaj. Jeśli jest to True przystępujemy do wykonywania bloku while. Po zakończeniu pracy tego bloku, warunek jest sprawdzany ponownie (w tym wypadku wartość zmiennej dzialaj). Jeżeli jest prawdziwy, pętla uruchamia się ponownie, w przeciwnym wypadku idziemy dalej.

Wyrażenia break i continue

Standardowe wykonanie pętli — warunek spełniony, wykonujemy cały blok, warunek spełniony, wykonujemy cały blok, ..., warunek niespełniony, koniec — nie zawsze jest pożądane. Pętlę można zarówno po prostu przerwać, jak też pominąć pozostałą do wykonania resztę bloku i rozpocząć następny obieg pętli.

Pętla while.svg

Wyrażenie break

Wyrażenia break używamy, aby wyrwać się z pętli, czyli zakończyć jej wykonywanie natychmiast. Przykład powyżej można w sposób równoważny, ale krótszy i czytelniejszy, zapisać z użyciem break.

Przykład

#!/usr/bin/python
# -*- coding: utf-8 -*-

liczba = 23
while True:
    strzal = int(raw_input('Wpisz liczbę całkowitą: '))
    if strzal == liczba:
        print 'Gratulacje, zgadłeś ją!\n(Ale nic nie wygrałeś.)'
	break
    elif strzal < liczba:
        print 'Nie, szukana liczba jest większa od podanej.'
    else:
        print 'Nie, szukana liczba jest mniejsza od podanej.'

print '\nKoniec programu.'


Wyrażenie continue

Wyrażenia continue używamy, aby nakazać Pythonowi ominąć pozostałe wyrażenia w bloku pętli i kontynuować od następnej iteracji tej pętli.

Jeszcze raz ten sam program, tym razem z wykorzystaniem continue:

Przykład

#!/usr/bin/python
# -*- coding: utf-8 -*-
# nazwa pliku: zgadywanka.py
liczba = 23
while True:
    strzal = int(raw_input('Wpisz liczbę całkowitą: '))
    if strzal > liczba:
        print 'Nie, ta liczba jest większa od szukanej.'
        continue
    if strzal < liczba:
        print 'Nie, ta liczba jest mniejsza od szukanej.'
        continue
    print 'Gratulacje, w końcu ją zgadłeś!'
    print '(Ale znowu nic nie wygrałeś.)'
    break
print '\nKoniec programu.'


Wyrażenia break i continue są uniwersalne — mogą zostać użyte zarówno w pętli while jak i w pętli for, opisanej poniżej.

Przykład: liczenie słów

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Nazwa pliku: continue.py

while True:
    s = raw_input('Wpisz coś: ')
    if s == 'quit':
        break
    if len(s) < 3:
        print 'Za krótkie.'
        continue
    print 'Wpis jest wystarczającej długości.'
    print 'Liczba słów wpisu:', len(s.split()), '.'
Rezultat:
$ python continue.py
Wpisz coś: a
Za krótkie.
Wpisz coś: 12
Za krótkie.
Wpisz coś: abc cde
Wpis jest wystarczającej długości.
Liczba słów wpisu: 2.
Wpisz coś: quit
Jak to działa?

W tym programie prosimy użytkownika o napis, ale zajmujemy się nim jedynie, gdy ma przynajmniej 3 znaki długości. (Zob. też #Jaką długość ma napis zawierający polskie litery? poniżej) Aby poznać długość napisu używamy wbudowanej funkcji len. Jeżeli jest on krótszy niż 3 znaki, wypisujemy komunikat Za krótkie., następnie wyrażenie continue powoduje pominięcie resztę poleceń w bloku pętli. Jeżeli jest on nie krótszy niż 3 znaki, wszystkie pozostałe linie w bloku pętli zostają wykonane.

Zadanie: sumator liczb dodatnich wersja 1

Wymyśl algorytm, a następnie napisz program, który sumuje liczby dodatnie podawane przez użytkownika.

  • Niech program prosi o podawanie kolejnych liczb dopóki użytkownik nie wprowadzi liczby ujemnej.
  • Niech po każdej podanej liczbie program wypisuje aktualną wartość sumy.
  • Na koniec niech program wypisuje całkowitą uzyskaną sumę.


Zadanie: sumator liczb dodatnich z licznikiem

Zmodyfikuj poprzedni algorytm tak, aby zliczał on ile liczb użytkownik wprowadził. Zatem chcemy mieć algorytm, który sumuje liczby dodatnie podawane przez użytkownika i zapamiętuje ile liczb zostało wprowadzonych.

  • Niech program prosi o podawanie kolejnych liczb dopóki użytkownik nie wprowadzi liczby ujemnej.
  • Niech po każdej podanej liczbie program wypisuje aktualną wartość sumy i informację ile liczb zostało zsumowanych.
  • Na koniec niech program wypisuje uzyskaną sumę i ilość wprowadzonych liczb


Zadanie: średnia liczb dodatnich

Zmodyfikuj poprzedni algorytm tak, że pobiera on od użytkownika ciąg liczb, i wypisuje długość i średnią tego ciągu. Przyjmij, że użytkownik wpisał wszystkie liczby wtedy, kiedy na pytanie o następną liczbę, poda liczbę ujemną.


Zadanie: średnia liczb

Zmodyfikuj poprzedni algorytm tak, że pobiera on od użytkownika ciąg liczb, i wypisuje długość i średnią tego ciągu. Przyjmij, że użytkownik wpisał wszystkie liczby wtedy, kiedy na pytanie o następną liczbę po prostu naciśnie Enter, czyli wpisze pustą linijkę.

Wskazówka: Fragment algorytmu potrzebny do obsługi wczytywania:

  1. wczytaj to co wprowadza użytkownik funkcją raw_input,
  2. następnie sprawdź czy to nie jest pusty napis.
    • Jeśli napis jest pusty to koniec,
    • a jeśli nie pusty to dokonaj konwersji na liczbę i kontynuuj program


Sekwencje i pętla for

Przedstawiona powyżej pętla while jest pierwowzorem pętli — na dodatek wygląda i działa niemalże tak samo w różnych językach programowania. Niemniej, na co dzień używa się raczej innej pętli, bardziej zwięzłej i wygodniejszej w użyciu.

 for zmienna in sekwencja:
     blok

Pętla for służy do wykonania tego samego bloku operacji dla każdego elementu z sekwencji. Oznacza to, że liczba wykonań bloku jest równa długości sekwencji. Zmienna przyjmuje po kolei wartości wszystkich elementów sekwencji. Jednokrotne wykonanie bloku pętli dla aktualnej wartości zmiennej nazywamy iteracją. W żargonie mówimy, że pętla for iteruje po sekwencji obiektów.

Najprostszym przykładem jest wykonanie tej samej operacji dla sekwencji liczb. W pythonie najprostszym przykładem sekwencji jest lista liczb. Można ją wytworzyć wpisując „ręcznie” liczby rozdzielone przecinkami w nawiasie kwadratowym. W powłoce pythona proszę wypróbować następujący kod:

a = [2, 3, 4, 5]
print 'sekwencja a:', a

Wygodniejszym sposobem wygenerowania potrzebnej sekwencji liczb jest funkcja range(a,b). Proszę wypróbować:

a = range(2,6)
print 'sekwencja a:', a


Przykład: wypisanie zawartości sekwencji element po elemencie

# cała sekwencja na raz
print 'sekwencja:', [2, 3, 4, 5]

# sekwencja element po elemencie
for i in [2, 3, 4, 5]:
    print 'element sekwencji:', i
Rezultat:
$ python for.py
sekwencja: [2, 3, 4, 5]
element sekwencji: 2
element sekwencji: 3
element sekwencji: 4
element sekwencji: 5
Jak to działa?

W tym programie wypisujemy listę liczb na dwa sposoby, za pierwszym razem od razu w całości, a za drugim razem element po elemencie.

Pętla for iteruje po liście w ten sposób, że przypisuje zmiennej i kolejny element listy, a następnie wykonuje blok wyrażeń.


Zadanie

Napisz dwa programy, które wypisują liczby całkowite od 1 do 10. W pierwszym wykorzystaj pętlę while, a w drugim pętlę for.


Zadanie

Napisz dwa programy, które obliczają sumę liczb całkowitych od 1 do 10. W pierwszym wykorzystaj pętlę while, a w drugim pętlę for.


Zadanie

Napisz program który pyta użytkownika o liczbę, a następnie oblicza silnię tej liczby. Wykorzystaj pętlę for.


Zadanie

Oblicz sumę sześcianów liczb naturalnych od 0 do 100.


Zadanie

Napisz program który sprawdzi, sześciany ilu liczb naturalnych (od 0) trzeba zsumować, by uzyskać liczbę większą niż [math]10^6[/math].


Wiele różnych typów sekwencji

Cechą wspólną wielu różnych kolekcji obiektów jest możliwość ponumerowania w określony sposób elementów i iteracji po nich, czyli dostępu do każdego z nich po kolei. Dzięki temu możemy np. iterować po sekwencji znaków, albo po sekwencji linii w pliku, albo po sekwencji liczb.

Najprostszy przykład sekwencji to lista wpisana bezpośrednio w tekście programu.

Drugi przykład, to ciąg znaków. Naturalnie myślimy o napisie jako o sekwencji znaków/liter.

Trzeci przykład, to funkcja range(a, b) generująca sekwencję liczb od a do b.

Sekwencje uzyskujemy też jako wynik wywołania wielu innych funkcji.

Funkcje range i xrange

Sekwencję liczb generujemy używając wbudowanej funkcji range. Podajemy dwie liczby, a funkcja range zwraca nam sekwencję liczb począwszy od pierwszej, a kończąc przed drugą z nich. Na przykład range(2,6) tworzy sekwencję [2,3,4,5]. Pierwszy argument range jest opcjonalny, jeśli go się nie poda, to sekwencja zaczyna się od 0. Na przykład wywołanie range(N) zwraca sekwencję N elementów: od 0 do N−1.

Domyślnie, range wytwarza sekwencję liczb zwiększających się co 1. Trzeci, opcjonalny argument range jest wartością o jaką będą zwiększane kolejne liczby w sekwencji. Na przykład range(1,5,2) zwróci [1,3].

Funkcja range tworzy od razu całą wymaganą listę. Jeśli lista jest długa, jest to niepotrzebny narzut, bo możemy przerwać wykonywanie pętli przedwcześnie, nie używając wszystkich elementów. Dlatego w wyrażeniu for należy używać zamiennika xrange, który zachowuje się tak samo, ale generuje swoje elementy w miarę potrzeby w trakcie wykonywania pętli.

Przykład

Potrzebujemy ciągu pierwszych 100 liczb parzystych. Piszemy

range(0, 2*100, 2)


Funkcja split

Metoda split dzieli ciąg znaków na słowa — tj. zwraca listę ciągów znaków dzieląc w miejscach gdzie występują spacje.

Przykład

# Plik slowa.py
for slowo in 'ala ma kota'.split():
    print slowo
Rezultat:
$ python slowa.py
ala
ma
kota


Podsumowanie

Pętla for...in działa dla każdej sekwencji. Tutaj mieliśmy sekwencje liczb, znaków lub napisów, ale tak naprawdę możemy użyć dowolnego rodzaju sekwencji z dowolnym rodzajem obiektów! Przyjrzymy się temu w następnych rozdziałach.

{{Wyjaśnienie|title=Uwaga dla programujących w C/C++/Java/C#==== Pythonowa pętla for radykalnie różni się od pętli for w C/C++. Programujący w C# zauważą, że Pythonowa pętla for jest podobna do pętli foreach w C#. Programujący w Java zauważą, ze jest podobna do pętli for(Object i : Iterable) w Java 1.5. W C/C++ pisze się for(int i = 0; i < 5; i++), zaś w Pythonie jedynie for i in range(0, 5).

Blok else po pętli

Blok else jest wywoływany gdy warunek pętli while przyjmuje wartość False lub gdy pętla for for przebiegnie całą sekwencję. To może się wydarzyć nawet za pierwszym razem, jeśli warunek jest od początku fałszywy albo sekwencja pusta. Jeżeli istnieje sekcja else dla pętli while, jest ona zawsze uruchamiana, z wyjątkiem sytuacji, gdy wyjdziesz z pętli poleceniem break.


Przykład:

Chcemy sprawdzić co jest mniejsze: liczba 10 czy piąta w kolejności liczba podzielna przez 3?

# Co jest mniejsze: 10 czy piąta w kolejności liczba podzielna przez 3?
# plik wyścig.py

podzielnych = 0
i = 1
while i < 10:
    if i % 3 == 0:
         print i, "jest podzielna przez 3"
         podzielnych += 1
         if podzielnych == 5:
              print "piąta liczba podzielna przez 3 jest mniejsza"
              break
    i += 1
else:
    print "doszliśmy do 10"
    print "10 jest mniejsze"
Rezultat:
$ python wyścig.py
3 jest podzielna przez 3
6 jest podzielna przez 3
9 jest podzielna przez 3
doszliśmy do 10
10 jest mniejsze

Zauważ, że gdy wyjdziesz z pętli for albo while używając break, blok else nie zostanie wywołany.

Pętle i litery z polskimi znakami diakrytycznymi

Zobaczmy jeszcze jeden przykład, wydawałoby się niemalże trywialny, na wykorzystanie pętli while...

Przykład

#!/usr/bin/python
# -*- coding: utf-8 -*-
# Nazwa pliku: break.py

while True:
    s = raw_input('Wpisz coś: ')
    if s == 'quit':
        break
    else:
	print 'Długość tego ciągu znaków to:', len(s)
print 'Koniec programu.'
Rezultat:
$ python break.py
Wpisz coś: Programming is fun
Długość tego ciągu znaków to: 18
Wpisz coś: When the work is done
Długość tego ciągu znaków to: 21
Wpisz coś: if you wanna make your work also fun:
Długość tego ciągu znaków to: 37
Wpisz coś:     use Python!
Długość tego ciągu znaków to: 15
Wpisz coś: quit
Koniec programu.
Jak to działa?

W tym programie wielokrotnie prosimy użytkownika o napisanie czegokolwiek i wypisujemy długość tego ciągu znaków na ekranie. Dodaliśmy specjalny warunek stopu poprzez sprawdzanie, czy użytkownik wpisał quit. Jeżeli tak, zatrzymujemy pętlę poleceniem break i wykonujemy końcową część programu.

Długość ciągu znaków sprawdzamy wbudowaną funkcją len.


I czy jest w tym coś podchwytliwego? Spróbujmy jeszcze raz:

$ python break.py
Wpisz coś: pyton
Długość tego ciągu znaków to: 5
Wpisz coś: wąż
Długość tego ciągu znaków to: 5
#                       dlaczego akurat 5 a nie 3?

Jaką długość ma napis zawierający polskie litery?

Jeśli zapytasz o długość ciągu znaków zawierającego polskie znaki diakrytyczne („ą”, „ć”, „ę”, „ł”, „ń”, „ó”, „ś”, „ź”, „ż” oraz „Ą”, i inne wielkie), uzyskana odpowiedź może różnić się od oczekiwań!

Przykład

$ python
>>> print len('aaa')
3
>>> print len('ąęó')
6                       #wynik może być różny w zależności od systemu: zazwyczaj 6 lub 3 lub 12

Aby wyjaśnić co się tutaj dzieje, trzeba by zacząć od podstaw, czyli stron kodowych i standardu Unicode. Temat jest ciekawy, ale by wyjaśnić powyższy wynik, wystarczy powiedzieć, że ciąg znaków 'ąęó' jest rozumiany przez Pythona jako ciąg znaków z bardzo ograniczonego zestawu ASCII. Jeśli używasz w miarę współczesnego systemu operacyjnego, to taka sekwencja znaków zostaje podana Pythonowi jako ciąg znaków ze znacznie szerszego zestawu Unicode. Znaki ASCII są zapisywanie po jednym na bajt, a że znaków Unicode jest znacznie więcej, to potrzeba na nie więcej miejsca. Ile dokładnie miejsca potrzeba na polskie znaki diakrytyczne zależy od systemu, ale najczęściej są to dwa bajty na każdy. Ostatecznie Python otrzymuje ciąg bajtów, które usiłuje interpretować jako jednobajtowe znaki ASCII. Rezultat jest taki, że otrzymujemy ilość bajtów zajmowanych przez ciąg znaków, a nie długość tego ciągu.

Nie tylko zliczanie znaków działa źle, pętla for też się gubi:

Przykład

$ python
>>> for znak in 'ąęó':
...     print znak
...
�                # znak oznaczający 'zepsuty' znak
�
�
�
�
�

Aby zaradzić temu zamieszaniu (np. bo nie chcemy żeby program dawał inny wynik w zależności od systemu na którym jest wykonywany) mamy proste wyjście. Musimy poinformować Pythona, że dany ciąg znaków jest ciągiem znaków Unicode. W tym celu ciąg przed otwierającym cudzysłowem wstawiamy literkę 'u':

Przykład

$ python
>>> print len(u'aaa')
3
>>> print len(u'ąęó')
3                     # zgodnie z oczekiwaniem
>>> for znak in u'ąęó':
...      print znak
...
ą
ę
ó

Niestety takie rozwiązanie jest pracochłonne dla programisty, a na dodatek łatwo o pomyłkę, gdy gdzieś zapomnimy przedrostka. Nowe wersje Pythona (3.0 i późniejsze) rozwiązują ten problem prościej — ciągi znaków to domyślnie Unicode i nie ma w ogóle przedrostka 'u'.

Podsumowanie

Zobaczyliśmy, jak używać trzech wyrażeń kontroli przepływu — if, while oraz for, razem z powiazanymi z nimi poleceniami break i continue. Są one jednymi z najczęściej używanych części Pythona i w związku z tym obeznanie się z nimi jest niezbędne.

W następnym rozdziale dowiemy się jak tworzyć funkcje i nimi operować.

Ćwiczenia

Zadanie: Elementy ciągu

Napisz program który zapyta użytkownika o liczbę, a następnie wyliczy dziesiąty element (wyraz a9) ciągu an = 3,9 · an–1 · (1 – an–1) zaczynając od wprowadzonej przez użytkownika liczby jako a0.

Potem popraw program tak, by sprawdzał czy wprowadzona liczba zawiera się w przedziale [0, 1] i pytał ponownie, aż do uzyskania liczby z tego przedziału.

Zadanie: Tabliczka mnożenia

Napisz program który wypisze elegancką tabliczkę mnożenia od 1 do 10.

Np.

  1   2   3   4   5   6   7   8   9  10
  2   4   6   8  10  12  14  16  18  20
  3   6   9  12  15  18  21  24  27  30
  4   8  12  16  20  24  28  32  36  40
  5  10  15  20  25  30  35  40  45  50
  6  12  18  24  30  36  42  48  54  60
  7  14  21  28  35  42  49  56  63  70
  8  16  24  32  40  48  56  64  72  80
  9  18  27  36  45  54  63  72  81  90
 10  20  30  40  50  60  70  80  90 100

Potem zmodyfikuj swój program tak, żeby użytkownik mógł podać zakres w jakim tabliczka ma być narysowana.


Zadanie: Kłopoty z kodem Pythona

Odpowiedz na list nieszczęśliwego użytkownika Pythona.

Witam, uczę się Pythona i mam kłopot z takim kawałkiem programu:

a = raw_input('Podaj liczbę: ')

if a > 0:
    print 'dodatnia'
elif a == 0:
    print 'zero'
elif a < 0:
    print 'ujemna'

Program działa, ale zawsze pisze "dodatnia" czy wpiszę liczbę dodatnią, ujemną czy zero. Wydaje mi się, że rozwiązanie problemu jest proste, ale nie mogę go znaleźć ;-)

Z góry dziękuję