Sposojeno predavanje o zankah - Ugibanje števila

Zanke: razlaga na težjem primeru

povzeto po gradivu prof. dr. J. Demšarja

Ponavljanje - malo bolj zapleteno

Igrajmo se ugibanje števil. Računalnik si bo izmislil število. Človek ga bo moral ugibati, računalnik pa mu bo povedal, ali je izmišljeno število večje ali manjše.

Najprej: kako naj si računalnik "izmisli naključno število". Tole je še kar zanimiv problem, vreden vsaj enega triurnega predavanja (ali tristo strani dolge knjige). Predavali in poslušali, brali in pisali so na srečo drugi. Mi pa moramo vedeti le čarobne besedeimport random, ki nam pričarajo funkcijo random.randint. Ta hoče dva argumenta in vrne naključno število med njima. Tako bo random.randint(10, 20) vrnila število med (vključno) 10 in 20.

import random
stevilo = random.randint(1100)
ugib = float(input("Ugibaj! "))
if stevilo > ugib:
    print("Več!")
else:
    print("Manj!")

Takšnale igra ne bo ravno prodajni hit, saj nimamo prav nobene možnosti, da uganemo številko, ki si jo je izmislil računalnik. Kot prvo, vsakič si izmisli novo številko - in "enkrat lahko ugibaš" ni ravno zabavna igra. Kot drugo, celo kadar jo uganemo, se bo zlagal in napisal "Manj!".

Najprej popravimo drugo težavo, saj je preprostejša.

import random
stevilo = random.randint(1100)
ugib = float(input("Ugibaj! "))
if stevilo > ugib:
    print("Več!")
else:
    if stevilo < ugib:
        print("Manj!")
    else:
        print("Bravo!")

Povejmo za priročno bližnjico: kombinacija else-if je tako pogosta, da obstaja zanjo okrajšava elif. S tem se znebimo tudi gnezdenja.

import random
stevilo = random.randint(1100)
ugib = float(input("Ugibaj! "))
if stevilo > ugib:
    print("Več!")
elif stevilo < ugib:
    print("Manj!")
else:
    print("Bravo!")

Pa za zdaj spreglejmo drugo težavo in za začetek uredimo, da bo človek lahko ugibal večkrat. Najprej "poskus obupanega programerja".

import random
stevilo = random.randint(1100)
ugib = float(input("Ugibaj! "))
if stevilo > ugib:
    print("Več!")
elif stevilo < ugib:
    print("Manj!")
else:
    print("Bravo!")
ugib = float(input("Ugibaj! "))
if stevilo > ugib:
    print("Več!")
elif stevilo < ugib:
    print("Manj!")
else:
    print("Bravo!")
ugib = float(input("Ugibaj! "))
if stevilo > ugib:
    print("Več!")
elif stevilo < ugib:
    print("Manj!")
else:
    print("Bravo!")
ugib = float(input("Ugibaj! "))
if stevilo > ugib:
    print("Več!")
elif stevilo < ugib:
    print("Manj!")
else:
    print("Bravo!")
ugib = float(input("Ugibaj! "))
if stevilo > ugib:
    print("Več!")
elif stevilo < ugib:
    print("Manj!")
else:
    print("Bravo!")

Ideja ni preveč posrečena. Tole je, v bistvu, "petkrat lahko ugibaš". No, seveda lahko dodamo še deset, petnajst ali sto kopij teh štirih vrstic, vendar ... to očitno ni to, ne? Da ne govorimo o tem, da po tem, ko uganemo število, še kar naprej sprašuje.

Tako kot lahko Pythonu naročimo, naj nekaj naredi če velja določen pogoj, mu lahko naročimo, naj nekaj dela, dokler velja določen pogoj. "Če" in "dokler" sta si povsem podobna: le namesto if pišemo while.

Potrebujemo torej tole:

import random
stevilo = random.randint(1100)
while ugib != stevilo:
    ugib = float(input("Ugibaj! "))
    if stevilo > ugib:
        print("Več!")
    elif stevilo < ugib:
        print("Manj!")
    else:
        print("Bravo!")

Dokler uporabnik ne zadane števila (ugib != stevilo), od njega zahtevamo, naj vpiše število, potem pa mu povemo, ali bi moral vpisati več, manj ali pa je zadel.

To je sicer videti imenitno, saj nam ni veš potrebno petkrat (dvajsetkrat, stokrat) pisati enih in istih vrstic programa. Nekoliko manj imenitno je dejstvo, da stvar ne deluje. Če ga poskušamo pognati, reče:

/Users/janez/env/orange3/bin/python /Users/janez/Desktop/teden_programiranja/ugibanje.py
Traceback (most recent call last):
  File "/Users/janez/Desktop/teden_programiranja/ugibanje.py", line 3in <module>
    while ugib != stevilo:
NameError: name 'ugib' is not defined

Hm. Ko pride program do vrstice while ugib != stevilo, ne pozna (vsaj tako trdi) spremenljivke ugib. Če razmislimo ... drži. Uporabnik bo prvič vpisal "ugib" šele eno vrstico kasneje. Naivna rešitev bi bila

import random
stevilo = random.randint(1100)
ugib = float(input("Ugibaj! "))
while ugib != stevilo:
    if stevilo > ugib:
        print("Več!")
    elif stevilo < ugib:
        print("Manj!")
    else:
        print("Bravo!")

Tole nikakor ne bo dobro, saj bo uporabnik tako vpisal številko le enkrat, potem pa mu bo zanka neskončnokrat zatulila "Več!", "Manj!" ali, če bo imel srečo (v nesreči), "Bravo!". Ne, ne, input mora biti v zanki.

Druga (na drugačen način ponesrečena) rešitev je:

import random
stevilo = random.randint(1100)
ugib = float(input("Ugibaj! "))
while ugib != stevilo:
    ugib = float(input("Ugibaj! "))
    if stevilo > ugib:
        print("Več!")
    elif stevilo < ugib:
        print("Manj!")
    else:
        print("Bravo!")

Tule uporabnik vpiše ugib pred zanko, vendar se program dela, kot da je slabo slišal in vpraša ponovno. Prvi vpis pa ignorira. Lahko pa se zgodi še nekaj bolj zabavnega: če uporabnik v prvem poskusu zadane število, se zanka ne bo izvedla nikoli, saj pogoj že v začetku ni izpolnjen (ugib ni različen od stevilo). V tem primeru se program tiho konča, ne da bi izpisal "Bravo!".

Dve rešitvi imamo. Obe sta prijetno zviti.

import random
stevilo = random.randint(1100)
ugib = float(input("Ugibaj! "))
while ugib != stevilo:
    if stevilo > ugib:
        print("Več!")
        ugib = float(input("Ugibaj ponovno! "))
    elif stevilo < ugib:
        print("Manj!")
        ugib = float(input("Ugibaj ponovno! "))
print("Bravo!")

Tale deluje tako: če uporabnik zadane že v prvem poskusu, se zanka ne izvede. Ker pa izpis "Bravo!" ni v zanki, temveč za njo, se bo čestitka vseeno izpisala. Če uporabnik ne zadane v prvem poskusu, pa se zanka začne vrteti in se vrti, dokler uporabnik ne zadane številke. Ko se to zgodi, se ne izpiše ne "Več!" (saj ne velja stevilo > ugib) ne "Manj!" (saj ne velja stevilo < ugib). Zanka se nato konča in izpiše se "Bravo!".

Tej rešitvi sicer nekoliko (no, precej) zamerimo, da se v njej trikrat pojavi ista vrstica, namreč input. Takšnih ponavljanj ne maramo.

Drugi način je bolj zvit.

import random
stevilo = random.randint(1100)
ugib = 0
while ugib != stevilo:
    ugib = float(input("Ugibaj! "))
    if stevilo > ugib:
        print("Več!")
    elif stevilo < ugib:
        print("Manj!")
    print("Bravo!")

Namesto da bi v začetku, pred zanko, vprašali uporabnika po številki, si gladko izmislimo, da je vpisal 0. Ker vemo, da je to narobe (iskana številka je med 1 in 100), smo lahko prepričani, da se bo zanka začela vrteti, vprašala uporabnika po številki in tako naprej.

Last modified: Wednesday, 7 April 2021, 7:37 AM