Zapiski
Pogojni stavki in pogoji
if
in else
Zadnji program s ponedeljkovih vaj, je zelo koristen, predvsem za tiste, ki imate doma top. Njegova koristnost pa je omejena z dejstvom, da vam pove le, kako daleč bo letela krogla, medtem ko v resnici s topom navadno poskušamo kaj zadeti. Dopolnimo ga, približajmo ga (ne ravno prehudemu) izzivu, ki sem ga zastavil študentom, ki bi jim bilo sicer dolgčas. Recimo, da uporabnik vnese še razdaljo do obstreljevane točke, program pa mu poleg dolžine strela pove še, ali je krogla priletela predaleč ali preblizu. To se naredi takole.
Tule se imamo pomeniti kup stvari. Opazili ste pojav praznih vrstic. Ničesar ne pomenijo. Dodajam jih lahko, kjer in kakor se mi zahoče. Njihov namen je narediti program preglednejši. Tule so trije ločeni bloki - prvi je samo uvoz matematičnih funkcij, drugi branje podatkov, tretji izračun in izpis. Ne bo vedno tako. Ko bomo bolj vešči programiranja, praznih vrstic ne bomo sejali tako na gosto, kot zgoraj., predvsem pa bloki v resničnih programih ne bodo tako kratki - tole je bolj za začetnike. Program si razbijte po lastnem okusu.
Največja novost v programu sta dve čarobni besedi: if
in else
. Takšnim besedam pravimo, kot smo se pogovarjali že prejšnjič, rezervirane besede, to pa zato, ker so rezervirane. Rezervirane v tem smislu, da nikoli ne moremo narediti spremenljivke (ali česa drugega) z imenom if
ali else
. (Poskusite napisati if=1
, pa boste videli, kako vas bo Python nakuril!) Mimogrede, poleg teh smo v program napisali še dve, namreč from
in import
, tudi tidve sta namreč rezervirani. Če je kdo hotel dodati na spisek še input
, ali print
ali, morda, float
, pa je v zmoti: input
je povsem običajna funkcija. Mirno lahko napišete input=1
, pa boste imeli spremenljivko z imenom input
(žal pa s tem s tem izgubili istoimensko funkcijo in vaš program ne more več ničesar vprašati uporabnika).
Besedi if
mora vedno slediti pogoj in nato dvopičje (to je tisto, zaradi česar je Python tako iz sebe, če za if
om napišemo enačaj). Koda (del programa), ki mu sledi, se bo izvedla le, če je pogoj resničen. Sicer pa ne. V našem primeru to pomeni, da bo Python izračunal, koliko predaleč je letela krogla (prevec
) in izpisal sporočilo o tem, le, če bo razdalja
večja kot zelena_razdalja
. if
-u sledi tudi else
, ki poskrbi za prekratke strele. V resnici else
niti ni nujen in ga izpustimo, kadar v nasprotnem primeru nimamo kaj povedati, kot, recimo, če bi zastavili konec programa takole:
Tule smo else
kar izpustili.
Prejšnjič sem prepovedal pisanje presledkov na začetku vrstice ... razen takrat, ko vam bom zapovedal, da jih pišite. No, napočil je ta trenutek: zapovedujem vam. Poglejte, kako sem oblikoval program: vrstice za if
-om sem nekoliko zamaknil. Koliko, ni pomembno, pomembno je samo, da sem vse zamaknil enako. (Nepisano pravilo: zamik naj bo velik štiri presledke. Ni obvezno, a če boste uporabljali štiri presledke, boste pisali enako kodo kot drugi. In nikoli nikoli ne uporabljajte tabulatorjev, ti naredijo zmedo!) Stavek else
sem poravnal z if
-om. Zakaj? Ker sodita skupaj, else
se nanaša na if
(to pa ni le "opcijsko", tako mora biti!). Za else
spet sledi zamik. Spet poljuben, a lepo je, da je enak kot prej (štiri presledke).
Kaj pa zadnji print
? Zadnji print
ni znotraj bloka else
, torej se izvede v vsakem primeru, ne glede na to, ali je bil pogoj izpolnjen ali ne.
Nekoliko off topic: Tako zamaknjenim delom programa pravimo, kot ste me že večkrat slišali reči, bloki. Bloke poznajo vsi normalni programski jeziki, le označujejo jih različno. Pogost način označevanja je označevanje z zavitimi oklepaji: v Cju (in iz njega izpeljanih oz. njemu podobnih jezikih C++, Java, C#, JavaScript, Php...) bi bili naši if
-i videti takole (poleg oklepajev je razlik še nekaj, vendar nas v tem trenutku ne zanimajo in jih spreglejte):
Za drugoverce, se pravi heretike, ki so vam zaviti oklepaji bolj všeč od zamikanja: najpogostejša napaka v naslednjih dveh tednih bo, da boste pozabljali dvopičje. Če se vaš program noče izvesti zaradi sintaktične napake, preverite dvopičja. Po dveh tednih se boste navadili.
Če smo že ravno pri verskih vprašanjih: v Pythonu ne pišemo oklepajev, kjer niso potrebni ... razen tam, kjer povečajo čitljivost. Noben greh ni napisati kot*(2*pi/360)
, čeprav bi se vse izračunalo popolnoma enako tudi, če oklepajev ne bi bilo. 2*pi/360
je namreč faktor, s katerim pretvarjamo iz stopinj v radiane in nič ni narobe, če je faktor obkrožen z oklepaji. V pogojnih izrazih pa oklepajev ne pišemo. Napisali bomo torej if razdalja < zelena_razdalja<%6%5%> in ne
if (razdalja < zelena_razdalja)<%6%5%>, kot bi morali pisati v nekaterih drugih jezikih. Oklepajev vam nihče ne brani, vendar vas bodo v teh, Pythonovskih krajih, čudno gledali, če boste preveč strašili z njimi.
Še ena estetska zadeva: za dvopičjem vedno pojdite v novo vrsto. Python sicer dovoli, da napišete tudi
Tule pa je še nekaj primerov napačno zamikanih programov.
Tretja vrstica mora imeti enak zamik kot druga.
Blok za stavkom if
mora biti zamaknjen.
if
in else
morata biti poravnana.
Če poskušamo zagnati kateregakoli od naštetih programov, bo Python javil sintaktično napako. Za razliko od običajnih napak, ki jih boste delali, in ko bodo programi naredili vsaj nekaj malega in potem crknili, ta ne bo niti trznil, temveč kar takoj javil napako. (PythonWin pa bo pomagal k vaši frustraciji s tem, da bo obvestil o napaki pokazal le v statusni vrstici in ga tudi prav hitro skril.) Spodnji program pa je sintaktično pravilen. Python ga lahko izvede. Najbrž pa to ni to, kar smo hoteli, saj se zadnji print
izvede le, če pogoj ni izpolnjen - da "je, kar je", bo program zatrdil le za prekratke, ne pa tudi za predolge strele.
Gnezdeni pogoji
Ob vsej svoji povečani uporabnosti program še vedno ni popoln: tako pesimističen in črnogled je, da ne predpostavlja, da bi se lahko zgodilo, da bi lahko cilj morda tudi zadeli. Vse topovske krogle letijo preblizu ali predaleč, kot bi morda potožila Levstik in Jurčič, če bi bila kdaj v življenju pisala o topovih (pa nista; vsaj ne, da bi bilo meni znano). Dodajmo torej še to reč.
(Ne spotaknite se ob dvojni enačaj; delajte se, da je samo eden. Čemu dva, bomo že še izvedeli.)
Razumevanje programa ni posebna kumšt. Ne smemo se ustrašiti tega, da se je znotraj if
a (oziroma, točneje, else
a) pojavil še en if
. Se zgodi; if
i znotraj if
ov so najobičajnejša stvar pod soncem. Ko je strah premagan, pa le upoštevamo, da se vse znotraj if
a oziroma else
a izvede le, če pogoj je oziroma ni izpolnjen.
Kot smo se naučili, ko smo if
in else
prvič uporabili, jima mora slediti zamaknjen blok. Isto pravilo velja tudi tu: če se if
že nahaja v zamaknjenem bloku (tako kot drugi if
v gornjem programu), bo drugi, notranji blok, pač še bolj zamaknjen.
Zamikanje v resnici uporabljamo tudi v jezikih, ki tega sicer ne zahtevajo (primer v Cju, ki smo ga napisali više, bi deloval tudi brez vsakega zamikanja, saj računalnik razbere strukturo blokov v programu s pomočjo zavitih oklepajev): zamiki so namenjeni (tudi) ljudem, saj povečajo čitljivost programa. Za blok, v katerem izračunamo prevec
in ga izpišemo, je že iz (nizkoletečega) aviona jasno, da je znotraj nečesa znotraj nečesa, v našem primeru znotraj if
a znotraj else
a. Ko bomo programirali zares, bomo zamikali in odmikali, da bo veselje.
V našem primeru se torej izpiše "Bum.", če smo top pravilno nastavili. V nasprotnem primeru (else
), pa se dogaja vse, kar smo napisali prej - program preveri, ali je krogla letela predaleč in se v tem primeru pritoži, da je letela predaleč, v nasprotnem primeru pa, da preblizu. "No, kar je, je" pa se izpiše v vsakem primeru - razen, če je krogla zadela cilj, saj se ta print
nahaja znotraj else
.
Sicerče
Nekateri programski jezik - in Python je med njimi - poznajo poleg if
in else
še elseif
oziroma elif
. (Še ena rezervirana beseda!) V Pythonu se uporablja slednja različica, elif
. V gornjem programu ga uporabimo takole:
Program se prebere čisto lepo, še lepše kot prej: če smo zadeli cilj, izpiši "Bum.", sicerče smo kroglo izstrelili predaleč, povej, da je letela predaleč, sicer povej, da preblizu. Pravzaprav bi mi bilo še bolj všeč malo obrnjeno.
Če je letela predaleč, povej, da predaleč; sicerče preblizu, povej, da preblizu; sicer zabumaj. Tole je nekako bližje načinu, na katerega razmišljamo - in programi bodo vedno jasnejši in imeli malo napak, če jih bomo pisali tako, kot sami razmišljamo (kadar nam različne okoliščine ne bodo tega preprečevale).
Po izkušnjah je elif
za študente huda, nenavadna skušnjava: nekateri ga pogosto uporabljajo namesto else
a. Recimo, da bi hoteli sprogramirati skrajšano pesimistično različico, po kateri krogla nima šans, da zadane cilj. Pravilni program bi bil takšen (ob predpostavki, da razdalja
ni nikoli enaka zelena_razdalja
):
Mnogi bi (iz meni neznanega razloga) naredili takole:
Program sicer deluje, vendar je čisto po nepotrebnem prezapleten: drugi pogoj je nepotreben. Če razdalja
ni manjša, je pač večja od zelena_razdalja
(ob predpostavki, seveda, da ni enaka).
Torej: ne zamenjujte else
in elif
.
Pogoji
Zdaj pa povejmo nekaj splošnejšega o pogojih. Kakšni vse so lahko? Najprej, pogoj je izraz. Torej nekaj, kar se da izračunati (po naši garažni definiciji izraza). Doslej so bili vsi naši izrazi nekako številski, aritmetični: v njih so nastopala števila in le-ta smo seštevali, množili, odštevali in kar se še takega dela s števili. Pogoji pa so logični izrazi. (V resnici se "logičnost" in "številskost" lahko poljubno prepletata, a to bomo mimogrede odkrili sproti.)
Rezultat logičnega izraza je logična vrednost. Logične vrednosti niso števila (torej niso int
ali float
) in niso nizi, torej so nekaj četrtega. Podatkovni tip, ki hrani logične vrednosti, se imenuje bool
(iz Boolean, po slovensko Booleov). Medtem ko je različnih števil (int
, float
) kar veliko (koliko, vam bo menda povedal kolega Fijavž, ko bo začel iz naftalina, pardon, formaldehida vlačiti Cantorja in njegov trop besnih alefov), ima tip bool
lahko le eno od dveh vrednosti, True
in False
. Kot smo mimogrede opazili prejšnji teden, sta True
in False
rezervirani besedi. (Če prav štejemo, smo naleteli že na sedem rezerviranih besed od 33! Saj bomo končali s predavanji, še preden se bodo začela!)
S števili že znamo računati. Z nizi tudi malo. Kako pa računamo z logičnimi vrednostmi? Menda jih ne moremo kar tako seštevati in odštevati?
Za zdaj se delajmo, da ne. Kasneje bomo videli, da zna Python z logičnimi izrazi početi še to in ono zanimivo reč, ki pa za začetnika niso tako pomembne.
Za logične izraze so nam na voljo trije operatorji: or
, and
in not
. Kaj pomenijo, je menda jasno, če je kdo v dvomih, pa naj se pouči ob prvem predavanju iz Diskretnih struktur.
(and
, or
in not
so rezervirane besed. 10 od 33!)
Operator not
ima prednost pred and
, and
pa pred or
. Spet si lahko pomagamo z oklepaji: a or b and c
pomeni isto kot a or (b and c)
bo resničen (True), če je resničen a
ali pa sta resnična b
in c
. Izraz (a or b) and c
pa bo resničen, če sta resnična a
ali b
(ali oba), poleg tega pa nujno še c
. Pri tem seveda predpostavljamo, da a
, b
in c
vsebujejo logične vrednosti.
Da bo reč lažje razumljiva, jo potrenirajmo na prav tem izrazu, vendar tako, da namesto a
, b
in c
oziroma namesto "golih" True
in False
pišemo bolj "konkretne" izraze.
Operatorja > in < pričakujeta na levi in desni neke reči, ki jih je mogoče primerjati; zadovoljna nista le s števili, temveč tudi z, recimo, nizi (primerjala jih bosta po abecedi), ne moreta pa primerjati števila z nizom. Njun rezultat je logična vrednost. Za "večje ali enako" in "manjše ali enako" uporabimo >=
in <=
.
Tudi operator za enakost smo že srečali, zdaj ga le še uradno predstavimo: če želimo preveriti enakost dveh števil (ali, v splošnem, enakost dveh stvari), uporabimo dvojni enačaj, ==
. Enojni enačaj je dobil svojo zadolžitev prejšnjo uro, namen je namreč prirejanju. Ali sta dve stvari različni, preverimo z !=
.
Ne zamenjujte dvojni in enojnih enačajev. Če pomotoma napišemo (a to se nam bo rekdo zgodilo),
a
-ju dvojke, temveč smo le preverili, ali je enak 2. Pogostejša napaka pa bo
if
pričakuje pogoj, mi pa smo napisali prireditveni stavek.
Kot občasno počnem, naj storim tudi ob tem času, namreč omenim druge jezike: večina tega je enaka v vseh jezikih. Za različnost imajo namesto !=
nekateri <>
. Namesto and
, or
in not
imajo mnogi jeziki &&
, ||
in !
. V Pythonu so se odločili za "besedne" variante, da so programi zračnejši.
Zdaj pa Pythonova posebnost: operatorje smemo nizati. Izraz 10 < T < 20
je resničen, če je T
med 10 in 20. Izraz 10 < T1 < T2 < 20
je resničen, če sta T1
in T2
med 10 in 20, pri čemer je T1
manjši od T2
. Izraz T1 < 10 < T2
je resničen, če je T1
manjši od 10, T2
pa večji od deset. 10 < T1 == T2 < 20
je resničen, če sta T1
in T2
enaka in sta nekje med 10 in 20.
Zdaj, ko to vemo, se lahko končno neham gristi v jezik v zvezi z ugotavljanjem, ali je krogla zadela cilj. Črnogledost je namreč na mestu: da bi krogla v resnici zadela cilj, je skoraj nemogoče, saj bomo vedno za kako decimalko mimo. Vendar smo zadovoljni tudi, če ga zgreši dovolj malo. Program bi se lahko napisalo tudi, recimo, tako:
Če vam smem, mladcem, kot star profesor prepustiti nekaj svoje (in hkrati tuje, splošno znane) modrosti: ne-celih števil (float
) nikoli ne smemo primerjati z operatorjem za enakost, temveč moramo vedno dovoliti tudi nekaj malega odstopanja. V jezikih, ki nimajo nizanja operatorjev, bi napisali nekaj v slogu
Kurzschluss
Tale tema je nekoliko bolj zapletena, vendar je prav, da tudi pri Programiranju 1 slišite kaj zapletenega. (Pa ne preveč.) No, v resnici pa vam moram tole povedati tudi zavoljo temeljitosti. Da mi ne bo kdo očital, da sem pri poučevanju šlampast. Sploh pa se je na prejšnjih predavanjih in vajah pokazalo, da ste pametnejših, kot smo pričakovali, in prav je, da ste za to vsaj malo tepeni.
Wikipedia definira kratek stik v električni napeljavi oz. vezju kot nekaj, kar dovoli elektriki, da potuje po krajši poti, kot je bila mišljena. Po domače, (kurzschluss je, ko se v televiziji nekaj zabliska, potem pa namesto, da bi delala, samo še smrdi. (Ravno v času prenavljanja tega sicer lani napisanega besedila imam v dnevni sobi na mizi primerek televizije, ki popolnoma ustreza gornjemu opisu.) Namesto da bi elektrika tekla čez transformatorje in druga navitja, steče po bližnjici.
Short-circuit behaviour, lahko bi ga prevedli kot kratkostično vedenje, v programskih jezikih pomeni, da po bližnjici steče izvajanje programa. Pri tem navadno ni ne bliskanja ne smradu, ker je takšno vedenje zaželeno. Za primer poglejmo malo spremenjeni gornji izraz: kako bi se računala vrednost (10 < 8 or 352 < 0) and 5 < 3
. 10 ni manj od 8 in 352 ni manj od 0. False or False
je False
. Prvi del izraza, vse, kar je pred and
-om je False
. Preden začne računalnik razmišljati o tem, ali je 5 več ali manj od 3, pa pomisli: zakaj bi to reč pravzaprav računal? Po tem, kar sem naračunal doslej, vem, da imam False and 5 < 3
. To bo neresnično v vsakem primeru, ne glede na to, ali je 5 več ali manj od 3. Zato drugega dela sploh ne računa, temveč že kar takoj vrne False
.
Pravilo je torej takšno: če je izraz, ki je levo od and
, neresničen, vrednosti izraza, ki je desno od and
, sploh ne računamo.
Podobno, le ravno obratno, bližnjico lahko uberemo pri or
. Kako bi računali vrednost 10 > 8 or 352 > 0
? Najprej ugotovimo, da je 10 res več kot 8. Moramo zdaj res preverjati še, ali je 352 več kot 0? Ne, saj že vemo, da bo izraz resničen. Kako pa je s 10 < 8 or 352 > 0
. Bomo, ko ugotovimo, da ni res, da je 10 manjše od 8, nehali računati? Ne, izraz je še vedno lahko resničen, reši nas lahko drugi del (in tudi v resnici nas). Pač pa bi pri izrazu 10 < 8 and 352 > 0
vrgli puško v koruzo že po prvem neuspehu, saj nas morebitni drugi uspeh ne more rešiti.
Pravilo kratkega stika si lahko zapomnimo na dva načina. Prvi, formalni, je tale: če je izraz levo od or
resničen, potem onega desno od or
ne preverjamo, saj že vemo, da bo rezultat resničen. Prav tako, če je izraz levo od and
neresničen, potem onega desno od njega ne preverjamo, saj že vemo, da bo rezultat neresničen. Drugi način je neformalen: izraz se računa od leve proti desni in samo, dokler je treba.
Študent pa se najbrž že sprašuje: me to res briga? Računalnik naj računa kar in kolikor hoče, meni je pomembno samo, da bo na koncu naračunal prav. V resnici je tako: če kratkega stika ne poznamo, nam ne bo nič hudega. Če ga, pa nam lahko pride prav. Poglejmo tale program, ki vpraša po številu psov in mačk. Kadar je psov vsaj dvakrat toliko kot mačk, bo rekel, da je psov veliko več. In obratno, če je mačk vsaj dvakrat toliko kot psov, bo povedal, da imamo veliko več mačk.
Najprej opazka: na tem mestu je prav, da smo uporabili elif
. Če bi rekli preprosto
if
je neresničen, saj psov ni dvakrat toliko kot mačk (10/8 je samo 1,25). Zato bi se izvedel else
in zmotno bi izvedeli, da je število mačk večje od števila psov - kar nikakor ni res.
Pač pa bi smeli namesto elif
mirno uporabiti if
. Razmislite.
Zdaj pa končno tisto, zaradi česar si primer pravzaprav ogledujemo: kaj se zgodi, če imamo 8 psov in 0 mačk? Nekaj takega.
Deljenje z 0. Da se temu izognemo, lahko napišemo takole:
... in potem še obratno. Ta program je sicer sorazmerno pravilen, vendar nekoliko dolgovezen. "Pravilen" je zato, ker smo z dodatnim preverjanjem poskrbeli, da ne pride do deljenja z 0. Samo "sorazmerno" pa zato, ker je psov velikokrat več kot mačk tudi takrat, kadar mačk ni, psi pa so (vsaj eden). Čisto prav je torej takole.
Grozno. Ampak pravilno. Veliko lepše pa je takole:
Logika je tale: kaj je potrebno, da bomo rekli, da je psov več kot mačk? Najprej: imeti moramo vsaj kakšnega psa. Če imamo več kot nič psov, pa bomo rekli, da je psov veliko več, če mačk sploh ni ali če jih je vsaj dvakrat toliko kot mačk. In tu je pomemben kratek stik: če bi se vedno izračunal celoten izraz, bi se tudi deljenje, psov/mack
, računalo tudi, kadar je število mačk enako 0.
Mimogrede: vedno koristi, če zna človek nekaj matematike. Gornji program se da pravzaprav napisati preprosteje, če se izognemo deljenju:
Tudi bere se čisto lepo: če imamo kakega psa in je psov vsaj dvakrat toliko kot mačk... in tako naprej.
Še nekaj snovi za veterane
Tole je pa res postranska zadeva, zanimiva predvsem za veterane iz drugih jezikov. Seveda je uporabna, obenem pa tako srčkana, da si jo moramo pogledati.
V Pythonu seveda velja, tako kot v Cju, da je 0 neresnična, vsa druga števila pa so resnična.
Prav tako veljajo za neresnične None
(None
je malo podoben Cjevskumu NULL
u, vendar ne preveč, saj Python nima kazalcev), neresničen je tudi prazen niz, prazen seznam... vse, kar je prazno. Do sem nič posebnega. Posebno pa je tole:
Znate razložiti, kako se to pravzaprav računa? Znate? Prav. Pa se vam zdi uporabno? Ne? Pa poglejte tole:
Reči moram, da v jeziki, ki niso Python, tole res pogrešam.
Naša prva zanka
Najprej malo refleksije. Kaj znamo narediti doslej: znamo napisati program, ki kaj vpraša, kaj računa in kaj izpiše. Naši programi se izvajajo od začetka do konca, s tem da lahko vmes preskočijo kak blok ukazov, ki se nahajajo znotraj kakega if
, else
ali elif
. Večino časa pa smo preživeli ob tem, kako napisati pogoj, se pravi, izraz, katerega vrednost je lahko resnično ali neresnično, True
ali False
. Zdaj pa se bomo naučili napisati programe, v katerih se del programa ponavlja.
V spodobnih programskih jezikih je to mogoče narediti na en in samo en način: z nečim, čemur mi rečemo zanka, Avstralci (razen aboriginov) pa loop. Jeziki imajo navadno več vrst zank, navadno tri. Python premore samo dve in danes bomo spoznali samo prvo: zanko while (while loop).
Tisočletna tradicija veleva, da mora biti začetnikov prvi program z zanko program, ki šteje do deset. V Pythonu je videti takole
Zanka while
je po svoje podobna stavku if
. Medtem ko se stavki znotraj if
izvedejo, če je pogoj resničen, se stavki znotraj while
ponavljajo dokler je pogoj resničen. "If" je "če" in "while" je "dokler", to vedo celo učiteljice angleščine. (Mimogrede, če že štejemo, while
je zadnja, enajsta rezervirana beseda danes. Smo že na tretjini!) Gornji program lahko torej kar dobesedno prevedemo iz Pythona v slovenščino:
K temu ni potrebna več nobena razlaga, ne?
Trenutek je tako dober kot katerikoli drugi, morda pa še boljši: tale x = x + 1
smo "prevedli" v "povečaj x za 1". V resnici nismo napisali tega, računalniku smo naročili, naj izračuna, koliko je x + 1
in to shrani nazaj v x
. Ker pa to reč tolikokrat potrebujemo in ker je lepo, da se stvar napiše tako, kot se misli - misli pa se "povečaj x za 1" -, obstaja tudi praktičnejši zapis: x += 1
. Točneje, napisali smo "k x prištej 1". Rečemo lahko tudi, x += 6
ali x += 1.13
; kaj to pomeni, je menda jasno. Podobno moremo pisati tudi x -= 1
(zmanjšaj x
za 1) ali, recimo x *= 2
, podvoji x
in x /= 10
, zdesetkaj x
. Pazite: +=
(in vse druge kombinacije) se štejejo kot ena beseda in med znaka +
in =
ne smemo napisati presledka.
Tisti, ki veste še kaj več: x++
boste v Pythonu zaman iskali. Nima ga, iz več razlogov, ki pa so malo pregloboki za nas tule.
Vrnimo se k topologiji. Uporabniku najbrž najeda živce, da mora program zaganjati znova in znova - poskusi nek kot in hitrost, programu mu odgovori le "premalo" ali "preveč" (ali, morda, bum) in se konča. Čemu ne bi ponavljal vpraševanja, dokler topničar ne ugane pravega kota?
Tako smo prišli do naloge, ki so jo nekateri reševali že doma. Top vedno strelja z isto hitrostjo, uporabnik jo vnese na začetku. Tudi razdaljo do cilja vpišemo le enkrat, saj predpostavimo, da cilj sedi pri miru. Nato program vedno znova sprašuje in sprašuje, pod kakšnim kotom želimo streljati, vse dokler ne ustrelimo ravno prav daleč.
Programček je prerasel že v pravo aplikacijo, dolg je postal. Začne se tako kot prej - vpraša za podatke in izračuna razdaljo. Potem pa ponavlja, dokler je razlika med želeno in naračunano razdaljo prevelika tole: izpiše, kako daleč bo letela krogla. Pove, ali bo to preblizu ali predaleč, potem prosi za nov kot in izračuna novo razdaljo.
(Nek študent me je (pravilno!) opozoril, da program še pred "Bum." izpiše "Predaleč, predaleč" ali "Preblizu, preblizu", tudi, kadar zadanemo cilj. Naj ostane, kot je, da ga ne bomo še bolj podaljševali. Za vajo pa naj ga študent sam popravi, na dva načina: eden je, da pred if
doda še en if
, drugi pa, da spremeni zanko tako, da while abs(zelena_razdalja - razdalja) > 0.001
zamenja z while True
in potem nekje v zanki uporabi break
, o katerem govorijo zapiski čez štirinajst dni.)
Topničar ima lahko seveda tudi srečo in že v prvem poskusu ugane pravilni kot. V tem primeru se zanka pač ne izvede nikoli, saj pogoj je, "dokler krogla ne zadane cilja," neresničen že kar takoj.
Dasiravno deluje, stari programerji ob tem ne čutimo posebnega zadovoljstva. Ni lepo, da se del programa ponavlja - namreč vpis kota in izračun. Ponavljanju kode se, če se le da, izognemo. V drugem semestru vas bo Dobravec podučil o zanki vrste do-while, Python pa je nima (in v resnici je skoraj nikoli ne potrebujemo - čeprav bi nam slučajno ravno tule res prišla prav). V Pythonu bi si pomagali z break
, ki pa je rezervirana beseda, in ker sem obljubil, da smo danes z njimi končali (pri nas držimo obljube!), bomo namesto trika z break
, ki si ga bomo prihranili za kdaj drugič, uporabili ščepec zvitosti.Program bomo preuredili.
Logika tega programa je za začetnika morda malo izkrivljena. Nič hudega, naj ga nekaj frustracije na predavanjih spodbudi k razmišljanju doma. Trik je namreč v tem: pred zanko ne računamo prave razdalje, temveč nastavimo razdaljo na vrednost, ki je zagotovo napačna. In napačno je v tem primeru prav: krogla zagotovo ne doseže cilja in zanka se gotovo začne izvajati. Pač pa zdaj že na začetku zanke vprašamo uporabnika za kot, nato izračunamo razdaljo in izpišemo, kar je treba.
Zdaj pa opremimo, kot se za vojsko spodobi, program še z geslom.
Tole je nekoliko nadležno. Če uporabnik ne ve gesla, ga program sprašuje in sprašuje in sprašuje in sprašuje in sprašuje in sprašuje in sprašuje in sprašuje in sprašuje in sprašuje... Lepše bi bilo, če bi ga vprašal le trikrat. To storimo takole.
Da se ne bo kdo, ki ve kaj več, spotikal ob program, mu povejmo: da, v modulu sys
je funkcija exit
. Običajno bi oni izpis "nič ne bo" zamenjali s sys.exit()
in se tako znebili potrebe po tem, da pišemo else
. Vendar vaši kolegi tega še ne vedo, zato jih s tem tudi ne bomo mučili.