1.3. Duży Lotek

Zakładamy, że znasz już podstawy podstaw :-) Pythona, czyli scenariusz Mały Lotek.

Jedna liczba to za mało, wylosujmy ich więcej! Zasady dużego lotka to typowanie 6 liczb z 49. Ponieważ trafienie jest tu bardzo trudne, napiszemy program w taki sposób, aby można było łatwo dostosować poziom jego trudności.

  1. Utwórz nowy plik toto2.py i uzupełnij go wymaganymi liniami wskazującymi interpreter pythona i użyte kodowanie.
  2. Wykorzystując funkcje raw_input() oraz int() pobierz od użytkownika ilość liczb, które chce odgadnąć i zapisz wartość w zmiennej ileliczb.
  3. Podobnie jak wyżej pobierz od użytkownika i zapisz maksymalną losowaną liczbę w zmiennej maksliczba.
  4. Na koniec wyświetl w konsoli komunikat “Wytypuj ileliczb z maksliczba liczb: ”.

Tip

Do wyświetlenia komunikatu można użyć konstrukcji: print "Wytypuj", ileliczb, "z", maksliczba, " liczb:". Jednak wygodniej korzystać z operatora %. Wtedy instrukcja przyjmie postać: print "Wytypuj %s z %s liczb: " % (ileliczb, maksliczba). Symbole zastępcze %s zostaną zastąpione kolejnymi wartościami z listy podanej po operatorze %. Najczęściej używamy symboli: %s – wartość zostaje zamieniona na napis przez funkcję str(); %d – wartość ma być dziesiętną liczbą całkowitą; %f – oczekujemy liczby zmiennoprzecinkowej.

1.3.1. Listy

1.3.1.1. Ćwiczenie

Jedną wylosowaną liczbę zapamiętywaliśmy w jednej zmiennej, ale przechowywanie wielu wartości w osobnych zmiennych nie jest dobrym pomysłem. Najwygodniej byłoby mieć jedną zmienną, w której można zapisać wiele wartości. W Pythonie takim złożonym typem danych jest lista.

Przetestuj w interpreterze następujące polecenia:

~$ python
>>> liczby = []
>>> liczby
>>> liczby.append(1)
>>> liczby.append(2)
>>> liczby.append(4)
>>> liczby.append(4)
>>> liczby
>>> liczby.count(1)
>>> liczby.count(4)
>>> liczby.count(0)

Tip

Klawisze kursora (góra, dół) służą w terminalu do przywoływania poprzednich poleceń. Każde przywołane polecenie możesz przed zatwierdzeniem zmienić używając klawiszy lewo, prawo, del i backspace.

Jak widać po zadeklarowaniu pustej listy (liczby = []), metoda .append() pozwala dodawać do niej wartości, a metoda .count() podaje, ile razy dana wartość wystąpiła w liście. To się nam przyda ;-)

Wróćmy do programu i pliku toto2.py, który powinien w tym momencie wyglądać tak:

Kod nr
1
2
3
4
5
6
7
8
9
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import random

ileliczb = int(raw_input("Podaj ilość typowanych liczb: "))
maksliczba = int(raw_input("Podaj maksymalną losowaną liczbę: "))
# print "Wytypuj", ileliczb, "z", maksliczba, " liczb:"
print "Wytypuj %s z %s liczb: " % (ileliczb, maksliczba)

Kodujemy dalej. Użyj pętli:

  • dodaj instrukcję for, aby wylosować ileliczb z zakresu ograniczonego przez maksliczba;
  • kolejne losowane wartości drukuj w terminalu;
  • sprawdź działanie kodu.

Trzeba zapamiętać losowane wartości:

  • przed pętlą zadeklaruj pustą listę;
  • wewnątrz pętli umieść polecenie dodające wylosowane liczby do listy;
  • na końcu programu (uwaga na wcięcia) wydrukuj zawartość listy;
  • kilkukrotnie przetestuj program.

1.3.2. Pętla while

Czy lista zawsze zawiera akceptowalne wartości?

../../_images/toto22_0.png

Pętla for nie nadaje się do unikalnych losowania liczb, ponieważ wykonuje się określoną ilość razy, a nie możemy zagwarantować, że losowane liczby będą za każdym razem inne. Do wylosowania podanej ilości liczb wykorzystamy więc pętlę while wyrażenie_logiczne:, która powtarza kod dopóki podane wyrażenie jest prawdziwe. Uzupełniamy kod w pliku toto2.py:

Kod nr
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import random

ileliczb = int(raw_input("Podaj ilość typowanych liczb: "))
maksliczba = int(raw_input("Podaj maksymalną losowaną liczbę: "))
# print "Wytypuj %s z %s liczb: " % (ileliczb, maksliczba)

liczby = []
# for i in range(ileliczb):
i = 0
while i < ileliczb:
    liczba = random.randint(1, maksliczba)
    if liczby.count(liczba) == 0:
        liczby.append(liczba)
        i = i + 1

print "Wylosowane liczby:", liczby

Losowane liczby zapamiętujemy w liście liczby. Zmienna i to licznik unikalnych wylosowanych liczb, korzystamy z niej w wyrażeniu warunkowym i < ileliczb, które kontroluje powtórzenia pętli. W instrukcji warunkowej wykorzystujemy funkcję zliczającą wystąpienia wylosowanej wartości w liście (liczby.count(liczba)), aby dodawać (liczby.append(liczba)) do listy tylko liczby wcześniej niedodane.

1.3.3. Zbiory

Przy pobieraniu typów użytkownika użyjemy podobnie jak przed chwilą pętli while, ale typy zapisywać będziemy w zbiorze, który z założenia nie może zawierać duplikatów (zob. zbiór).

1.3.3.1. Ćwiczenie

W interpreterze Pythona przetestuj następujące polecenia:

~$ python
>>> typy = set()
>>> typy.add(1)
>>> typy.add(2)
>>> typy
>>> typy.add(2)
>>> typy
>>> typy.add(0)
>>> typy.add(9)
>>> typy

Pierwsza instrukcja deklaruje pusty zbiór (typy = set()). Metoda .add() dodaje do zbioru elementy, ale nie da się dodać dwóch takich samych elementów. Drugą cechą zbiorów jest to, że ich elementy nie są w żaden sposób uporządkowane.

Wykorzystajmy zbiór, aby pobrać od użytkownika typy liczb. W pliku toto2.py dopisujemy:

Kod nr
20
21
22
23
24
25
26
27
print "Wytypuj", ileliczb, "z", maksliczba, "liczb:"
typy = set()
i = 0
while i < ileliczb:
    typ = raw_input("Podaj liczbę %s: " % (i + 1))
    if typ not in typy:
        typy.add(typ)
        i = i + 1

Zauważ, że operator in pozwala sprawdzić, czy podana liczba jest (if typ in typy) lub nie (if typ not in typy:) w zbiorze. Przetestuj program.

1.3.4. Operacje na zbiorach

Określenie ilości trafień w większości języków programowania wymagałoby przeszukiwania listy wylosowanych liczb dla każdego podanego typu. W Pythonie możemy użyć arytmetyki zbiorów: wyznaczymy część wspólną.

1.3.4.1. Ćwiczenie

W interpreterze przetestuj poniższe instrukcje:

~$ python
>>> liczby = [1,3,5,7,9]
>>> typy = set([2,3,4,5,6])
>>> set(liczby) | typy
>>> set(liczby) - typy
>>> trafione = set(liczby) & typy
>>> len(trafione)

Polecenie set(liczby) przekształca listę na zbiór. Kolejne operatory zwracają sumę (|), różnicę (-) i iloczyn (&), czyli część wspólną zbiorów. Ta ostania operacja bardzo dobrze nadaje się do sprawdzenia, ile liczb trafił użytkownik. Funkcja len() zwraca ilość elementów każdej sekwencji, czyli np. napisu, listy i zbioru.

Do pliku toto2.py dopisujemy:

Kod nr
31
32
33
34
35
36
trafione = set(liczby) & typy
if trafione:
    print "\nIlość trafień: %s" % len(trafione)
    print "Trafione liczby: ", trafione
else:
    print "Brak trafień. Spróbuj jeszcze raz!"

Instrukcja if trafione: sprawdza, czy część wspólna zawiera jakiekolwiek elementy. Jeśli tak, drukujemy liczbę trafień i trafione liczby.

Przetestuj program dla 5 typów z 10 liczb. Działa? Jeśli masz wątpliwości, wpisz wylosowane i wytypowane liczby w interpreterze, np.:

>>> liczby = [1,4,2,6,7]
>>> typy = set([1,2,3,4,5])
>>> trafione = set(liczby) & typy
>>> if trafione:
...   print len(trafione)
...
>>> print trafione

Wnioski? Logika kodu jest poprawna, czego dowodzi test w terminalu, ale program nie działa. Dlaczego?

Tip

Przypomnij sobie, jakiego typu wartości zwraca funkcja raw_input() i użyj we właściwym miejscu funkcji int().

Wynik działania programu powinien wyglądać następująco:

../../_images/toto25.png

1.3.4.2. Do 3 razy sztuka

Zastosuj pętlę for tak, aby użytkownik mógł 3 razy typować liczby z tej samej serii liczb wylosowanych. Wynik działania programu powinien przypominać poniższy zrzut:

../../_images/toto26.png

1.3.5. Błędy

Kod naszego programu do tej pory przedstawia się mniej więcej tak:

Kod nr
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import random

ileliczb = int(raw_input("Podaj ilość typowanych liczb: "))
maksliczba = int(raw_input("Podaj maksymalną losowaną liczbę: "))

liczby = []
i = 0
while i < ileliczb:
    liczba = random.randint(1, maksliczba)
    print "Wylosowane liczba: %s " % liczba
    if liczby.count(liczba) == 0:
        liczby.append(liczba)
        i = i + 1

for i in range(3):
    print "Wytypuj %s z %s liczb: " % (ileliczb, maksliczba)
    typy = set()
    i = 0
    while i < ileliczb:
        typ = int(raw_input("Podaj liczbę %s: " % (i + 1)))
        if typ not in typy:
            typy.add(typ)
            i = i + 1

    trafione = set(liczby) & typy
    if trafione:
        print "\nIlość trafień: %s" % len(trafione)
        print "Trafione liczby: ", trafione
    else:
        print "Brak trafień. Spróbuj jeszcze raz!"

    print "\n" + "x" * 40 + "\n"  # wydrukuj 40 znaków x

print "Wylosowane liczby:", liczby

Uruchom powyższy program i podaj ilość losowanych liczb większą od maksymalnej losowanej liczby. Program wpada w nieskończoną pętlę! Po chwili zastanowienia dojdziemy do wniosku, że nie da się wylosować np. 6 unikalnych liczb z zakresu 1-5.

1.3.5.1. Ćwiczenie

  • Użyj w kodzie instrukcji warunkowej, w przypadku gdy użytkownik chciałby wylosować więcej liczb niż podany zakres maksymalny, wyświetli komunikat “Błędne dane!” i przerwie wykonywanie programu za pomocą funkcji exit().

1.3.6. Wyjątki

Testujemy dalej. Uruchom program i zamiast liczby podaj tekst. Co się dzieje? Uruchom jeszcze raz, ale tym razem jako typy podaj wartości spoza zakresu <0;maksliczba>. Da się to zrobić?

Jak pewnie zauważyłeś, w pierwszym wypadku zgłoszony zostaje wyjątek ValuError (zob.: wyjątki) i komunikat invalid literal for int() with base 10, który informuje, że funkcja int() nie jest w stanie przekształcić podanego ciągu znaków na liczbę całkowitą. W drugim wypadku podanie nielogicznych typów jest możliwe.

Uzupełnijmy program tak, aby był nieco odporniejszy na niepoprawne dane:

Kod nr
 6
 7
 8
 9
10
11
12
13
14
try:
    ileliczb = int(raw_input("Podaj ilość typowanych liczb: "))
    maksliczba = int(raw_input("Podaj maksymalną losowaną liczbę: "))
    if ileliczb > maksliczba:
        print "Błędne dane!"
        exit()
except ValueError:
    print "Błędne dane!"
    exit()

Do przechwytywania wyjątków używamy konstrukcji try: ... except: ..., czyli: spróbuj wykonać kod w bloku try, a w razie błędów przechwyć wyjątek i wykonaj podporządkowane instrukcje. W powyższym przypadku wyświetlamy odpowiedni komunikat i kończymy działanie programu (exit()).

Pobierając typy od użytkownika również musimy spróbować przekształcić podane znaki na liczbę i w razie błędu przechwycić wyjątek. Poza tym trzeba sprawdzić, czy użytkownik podaje sensowne typy. Uzupełniamy kod:

Kod nr
28
29
30
31
32
33
34
35
36
37
    while i < ileliczb:
        try:
            typ = int(raw_input("Podaj liczbę %s: " % (i + 1)))
        except ValueError:
            print "Błędne dane!"
            continue

        if 0 < typ <= maksliczba and typ not in typy:
            typy.add(typ)
            i = i + 1

Nowością w powyższym kodzie jest instrukcja continue. Inaczej niż break nie przerywa ona działania pętli, ale rozpoczyna jej wykonywanie od początku ignorując następujące po niej komendy.

Drugą nowością jest warunek if 0 < typ <= maksliczba:. Jest to skrócony zapis wyrażenia logicznego z użyciem operatora koniunkcji: typ > 0 and typ <= maksliczba. Sprawdzamy w ten sposób czy wartość zmiennej typ jest większa od zera i mniejsza lub równa wartości zmiennej maksliczba.

1.3.7. Materiały

Źródła:


Licencja Creative Commons Materiały Python 101 udostępniane przez Centrum Edukacji Obywatelskiej na licencji Creative Commons Uznanie autorstwa-Na tych samych warunkach 4.0 Międzynarodowa.

Utworzony:2017-09-08 o 19:38 w Sphinx 1.4.5
Autorzy:Patrz plik “Autorzy”