Struktury kontrolne¶
Zauważyliśmy wcześniej, że algorytmy wymagają dwóch ważnych struktur kontrolnych: iteracji i selekcji. Oba te typy wspierane są w Python w różnych formach. Programista dokonuje wyboru, które wyrażenie jest najbardziej użyteczne w danym momencie programu.
Dla iteracji Python udostępnia podstawową formę instrukcji while oraz zaawansowaną, rozszerzoną funkcjonalnie w stosunku do innych języków programowania instrukcję for. Pętla while powtarza zawarty w niej kod tak długo, aż warunek przetwarzania jest prawdą. Na przykład:
>>> counter = 1
>>> while counter <= 5:
... print("Hello, world")
... counter = counter + 1
Hello, world
Hello, world
Hello, world
Hello, world
Hello, world
wypisuje frazę “Hello, world” pięć razy. Warunek wyrażenia while jest obliczany na początku każdej iteracji. Jeśli jest prawdą (True) kod zawarty w pętli jest wykonany. Pełna budowa struktry while w języku Python jest widoczna dzięki narzuceniu obowiązkowych wcięć w liniach kodu.
Pętla while jest instrukcją iteracyjną ogólnego przeznaczenia, którą będziemy używać w wielu różnych algorytmach. W wielu przypadkach warunkiem wykonania będzie złożone wyrażenie takie jak:
while counter <= 10 and not done:
...
Wykonanie kodu następuje tylko wtedy, jeśli obie części warunku są spełnione. Wartość zmiennej counter musi być mniejsza bądź równa 10, a zmienna done musi być równa False (negacja False równa się True), aby ich rezultat dał prawdę (True i True = True)
Chociaż powyższa konstrukcja jest bardzo użyteczne w wielu sytuacjach zachodzi potrzeba użycia instrukcji iterracyjnej for, którą można w prosty sposób łączyć z kolekcjami Python. Wyrażenie for pozwala na iterowanie po elementach kolekcji tak długo, dopóki jest ona sekwencją. Na przykład:
>>> for item in [1,3,6,2,5]:
... print(item)
...
1
3
6
2
5
przypisuje zmiennej item kolejną wartość z listy [1,3,6,2,5], a następnie kod w pętli jest wykonany. Działanie to można zastosować do każdej kolekcji, która jest sekwencją czyli: krotek, list, i stringów).
Najczęstszym użyciem pętli for jest iteracja po zdefiniowanym zakresie wartości. Wyrażenie:
>>> for item in range(5):
... print(item**2)
...
0
1
4
9
16
>>>
wykona funkcję print pięć razy. Funkcja range zwróci obiekt będący zbiorem sekwencji 0,1,2,3,4. Każda wartość przypisana zostanie zmiennej item, a następnie podniesiona do kwadratu i wyświetlona.
Innym częstym zastosowaniem tego rodzaju struktury licznikowej jest przetwarzanie każdego kolejnego znaku w stringu. Kolejny fragment kodu iteruje po liście (wordlist), zawierającej łańcuchy znaków. Dla każdego z tych łańcuchów wyodrębnia kolejny jego znak, a następnie dodaje go do listy (letterlist), która końcowo zawierać będzie wszystkie znaki podanych słów.
Instrukcje warunkowe umożliwiają programiście zadanie pytania, którego odpowiedź posłuży do podjęcia decyzji jakiego rodzaju działanie ma zostać wykonane. Większość języków programowania wspiera dwie wersje tej użytecznej konstrukcji: ifelse oraz if. Prosta selekcja binarna może być przeprowadzona za pomocą wyrażenia ifelse:
if n<0:
print("Sorry, value is negative")
else:
print(math.sqrt(n))
W tym przykładzie obiekt, którego referencją jest n zostaje sprawdzony czy jest mniejszy od zera. Jeśli tak to wypisuje wiadomość, że jest ujemna. Jeśli nie wykonane zostaną instrukcje zawarte po klauzuli else operacja podniesionia do kwadratu i wypisania wyniku na ekranie.
Instrukcje warunkowe, jak każde inne struktury kontrolne, można zagnieżdżać tak, by rezultat jednej wpływał na rodzaj pytania kolejnej. Na przykład załóżmy, że score jest zmienną, która posiada referencje do wyniku testu z informatyki.
if score >= 90:
print('A')
else:
if score >=80:
print('B')
else:
if score >= 70:
print('C')
else:
if score >= 60:
print('D')
else:
print('F')
Fragment też klasyfikuje wartość score poprzez wypisanie uzyskanej oceny w formie litery. Jeśli jest większe lub równe 90 wyrażenie print A jest wykonane. Jeśli nie (else) kolejny warunek jest sprawdzany. Jeśli score jest większy lub równy 80 oznacza to, że znajduje się w zakresie pomiędzy 80 a 89, ponieważ rezultatem wcześniejszego warunku była nieprawda (False). W tym przypadku wypisany zostanie znak B. Zauważ, że struktura blokowa składni Python (obowiązkowe wcięcia) pomagają nadać sens kolejnym fragmentom kodu, zawartym pomiędzy if i else bez potrzeba użycia innych elementów syntaktycznych.
Alternatywnym zapisem tego typu zagnieżdżonych selekcji jest użycie instrukcji elif. Słowa kluczowe else oraz if są tu połączone w celu wyeliminowana dodatkowego poziomu wcięcia. Zapamiętaj jednak, że końcowe else jest nadal wymagane w celu określenia drogi wykonania programu, gdy wszystkie wcześniejsze warunki zostaną odrzucone (okażą się fałszywe).
if score >= 90:
print('A')
elif score >=80:
print('B')
elif score >= 70:
print('C')
elif score >= 60:
print('D')
else:
print('F')
Python posiada również konstrukcje selekcji bez alternatywy if. Wyrażenie to pozwala na wykonanie zdefiniowanej operacji tylko w sytuacji, gdy wartością logiczną warunku jest prawda. W przypadku, gdy wartość ta to fałsz program pomija zestaw instrukcji zawartych w bloku if i przechodzi do kolejnej następującej po jego końcu. W poniższym przykładzie zmienna n jest sprawdzana pod kątem jej ujemności. Jeśli jest, to zostaje zmodyfikowana przez funkcję, zwracającą wartość bezwzględną podanego argumentu. Następna instrukcja oblicza pierwiastek kwadratowy.
if n<0:
n = abs(n)
print(math.sqrt(n))
Self Check
Test your understanding of what we have covered so far by trying the following exercise. Modify the code from Activecode 8 so that the final list only contains a single copy of each letter.
Wracając do list, alternatywną metodą tworzenia listy jest użycie instrukcji iteracji i selekcji. Nazwane jest to wyrażeniem listowym. Wyrażenie listowe pozwala w prosty sposób stworzyć listę na bazie zdefiniowanych operacji i kryteriów wykonania. Na przykład: jeśli chcielibyśmy zbudować listę potęg kwadratowych pierwszych 10 liczb całkowitych można posłużyć się for w zwyczajny sposób:
>>> sqlist=[]
>>> for x in range(1,11):
sqlist.append(x*x)
>>> sqlist
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
>>>
Za pomocą wyrażenia listowego wygląda to w takiej formie:
>>> sqlist=[x*x for x in range(1,11)]
>>> sqlist
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
>>>
Zmienna x przyjmuje wartości od 1 do 10 zgodnie ze specyfikacją konstrukcji for. Następnie obliczana jest wartość x*x i dodana do listy właśnie konstruowanej. Składnia wyrażeń listowych pozwala na zawarcie również struktur warunków/selekcji dzięki czemu możliwe jest szczegółowe zdefiniowanie wartości jakie mają zostać dołączone. Dla przykładu:
>>> sqlist=[x*x for x in range(1,11) if x%2 != 0]
>>> sqlist
[1, 9, 25, 49, 81]
>>>
Wyrażenie listowe utworzyło listę, która zawiera potęgi liczb całkowitych niepodzielnych przez 2 w zbiorze od 1 do 10. Każda sekwencja, wspierająca iteracje może zostać zawarta w wyrażeniu listowym w celu budowy nowej listy.
>>>[ch.upper() for ch in 'comprehension' if ch not in 'aeiou']
['C', 'M', 'P', 'R', 'H', 'N', 'S', 'N']
>>>
Self Check
Test your understanding of list comprehensions by redoing Activecode 8 using list comprehensions. For an extra challence, see if you can figure out how to remove the duplicates.