1.2. 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.

Na początek

  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 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: ”.

Wskazówka

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.2.1. Listy

1.2.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:

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

Wskazówka

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 python3
# -*- coding: utf-8 -*-

import random

ileliczb = int(input("Podaj ilość typowanych liczb: "))
maksliczba = int(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.2.2. Pętla while

Czy lista zawsze zawiera akceptowalne wartości?

../../_images/toto22_0.png

Pętla for nie nadaje się do losowania unikalnych 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 python3
# -*- coding: utf-8 -*-

import random

ileliczb = int(input("Podaj ilość typowanych liczb: "))
maksliczba = int(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.2.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.2.3.1. Ćwiczenie

W interpreterze Pythona przetestuj następujące polecenia:

~$ python3
>>> 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 %s z %s liczb: " % (ileliczb, maksliczba))
typy = set()
i = 0
while i < ileliczb:
    typ = 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.2.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.2.4.1. Ćwiczenie

W interpreterze przetestuj poniższe instrukcje:

~$ python3
>>> liczby = [1,3,5,7,9]
>>> typy = set([2,3,4,5,6])
>>> set(liczby) | typy
>>> set(liczby) - typy
>>> trafione = set(liczby) & typy
>>> trafione
>>> 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 czy 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?

Wskazówka

Przypomnij sobie, jakiego typu wartości zwraca funkcja 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.2.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.2.5. Błędy i wyjątki

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 python3
# -*- coding: utf-8 -*-

import random

ileliczb = int(input("Podaj ilość typowanych liczb: "))
maksliczba = int(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(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.2.5.1. Ćwiczenie

  • Użyj w kodzie instrukcji warunkowej, która 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().

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
 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
38
39
40
41
42
43
44
45
46
47
48
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import random

try:
    ileliczb = int(input("Podaj ilość typowanych liczb: "))
    maksliczba = int(input("Podaj maksymalną losowaną liczbę: "))
    if ileliczb > maksliczba:
        print("Błędne dane!")
        exit()
except ValueError:
    print("Błędne dane!")
    exit()

liczby = []
i = 0
while i < ileliczb:
    liczba = random.randint(1, maksliczba)
    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:
        try:
            typ = int(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

    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)

Do przechwytywania wyjątków używamy konstrukcji try: ... except wyjątek: ..., czyli: spróbuj wykonać kod w bloku try, a w razie błędów przechwyć wyjątek i wykonaj podporządkowane instrukcje. W powyższych przypadkach przechwytujemy wyjątek ValueError, wyświetlamy odpowiedni komunikat i kończymy działanie programu (exit()) lub wymuszamy ponowne wykonanie pętli (continue) zamiast ją przerywać (break).

Poza tym sprawdzamy, czy użytkownik podaje sensowne typy. Warunek if 0 < typ <= maksliczba: 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.2.6. 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:2022-05-22 o 19:52 w Sphinx 1.5.3
Autorzy:Patrz plik “Autorzy”