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.

from math import * g = 10 kot = float(input("Vnesi kot (v stopinjah)")) v = float(input("Vnesi hitrost (m/s)")) zelena_razdalja = float(input("Vnesi želeno razdaljo (m)")) kot_rad = kot * 2*pi/360 razdalja = v**2 * sin(2*kot_rad) / g print("Kroglo bo odneslo", s, "metrov") if razdalja > zelena_razdalja: prevec = zelena_razdalja - razdalja print("Predaleč, predaleč: krogla bo preletela cilj za", prevec, "metrov") else: premalo = razdalja - zelena_razdalja print("Preblizu, preblizu: krogla bo letela", premalo, "metrov premalo") print("No, kar je, je.")

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 ifom 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:

print("Kroglo bo odneslo", s, "metrov") if razdalja > zelena_razdalja: print("Hu, to je bilo pa res daleč!")

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):

if (razdalja > zelena_razdalja) { prevec = razdalja - zelena_razdalja; printf("Predalec, predalec: krogla bo preletela cilj za %.3f metrov\n", prevec); } else { premalo = zelena_razdalja - razdalja; printf("Preblizu, preblizu: krogla bo letela %.3f metrov premalo\n", premalo); } printf("No, kar je, je.\n"); Kaj je lepše, je stvar okusa in predmet številnih verskih vojn.

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

if razdalja > zelena_razdalja: print ("Predaleč") vendar to vodi v nepregledne programe. To se ne dela.

Tule pa je še nekaj primerov napačno zamikanih programov.

if razdalja > zelena_razdalja: prevec = zelena_razdalja - razdalja print("Predaleč, predaleč: krogla bo preletela cilj za", prevec, "metrov") else: premalo = razdalja - zelena_razdalja print("Preblizu, preblizu: krogla bo letela", premalo, "metrov premalo") print("No, kar je, je.")

Tretja vrstica mora imeti enak zamik kot druga.

if razdalja > zelena_razdalja: prevec = zelena_razdalja - razdalja print("Predaleč, predaleč: krogla bo preletela cilj za", prevec, "metrov")

Blok za stavkom if mora biti zamaknjen.

if razdalja > zelena_razdalja: prevec = zelena_razdalja - razdalja print("Predaleč, predaleč: krogla bo preletela cilj za", prevec, "metrov") else: premalo = razdalja - zelena_razdalja print("Preblizu, preblizu: krogla bo letela", premalo, "metrov premalo") print("No, kar je, je.")

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.

if razdalja > zelena_razdalja: prevec = zelena_razdalja - razdalja print("Predaleč, predaleč: krogla bo preletela cilj za", prevec, "metrov") else: premalo = razdalja - zelena_razdalja print("Preblizu, preblizu: krogla bo letela", premalo, "metrov premalo") print("No, kar je, je.")

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

from math import * g = 10 kot = float(input("Vnesi kot (v stopinjah)")) v = float(input("Vnesi hitrost (m/s)")) zelena_razdalja = float(input("Vnesi želeno razdaljo (m)")) kot_rad = kot * 2*pi/360 razdalja = v**2 * sin(2*kot_rad) / g print("Kroglo bo odneslo", s, "metrov") if razdalja == zelena_razdalja: print("Bum.") else: if razdalja > zelena_razdalja: prevec = zelena_razdalja - razdalja print("Predaleč, predaleč: krogla bo preletela cilj za", prevec, "metrov") else: premalo = razdalja - zelena_razdalja print("Preblizu, preblizu: krogla bo letela", premalo, "metrov premalo") print("No, kar je, je.")

(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 ifa (oziroma, točneje, elsea) pojavil še en if. Se zgodi; ifi znotraj ifov so najobičajnejša stvar pod soncem. Ko je strah premagan, pa le upoštevamo, da se vse znotraj ifa oziroma elsea 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 ifa znotraj elsea. 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:

from math import * g = 10 kot = float(input("Vnesi kot (v stopinjah)")) v = float(input("Vnesi hitrost (m/s)")) zelena_razdalja = float(input("Vnesi želeno razdaljo (m)")) kot_rad = kot * 2*pi/360 razdalja = v**2 * sin(2*kot_rad) / g print("Kroglo bo odneslo", s, "metrov") if razdalja == zelena_razdalja: print("Bum.") elif razdalja > zelena_razdalja: prevec = zelena_razdalja - razdalja print("Predaleč, predaleč: krogla bo preletela cilj za", prevec, "metrov") else: premalo = razdalja - zelena_razdalja print("Preblizu, preblizu: krogla bo letela", premalo, "metrov premalo") print("No, kar je, je.")

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.

from math import * g = 10 kot = float(input("Vnesi kot (v stopinjah)")) v = float(input("Vnesi hitrost (m/s)")) zelena_razdalja = float(input("Vnesi želeno razdaljo (m)")) kot_rad = kot * 2*pi/360 razdalja = v**2 * sin(2*kot_rad) / g print("Kroglo bo odneslo", s, "metrov") if razdalja > zelena_razdalja: prevec = zelena_razdalja - razdalja print("Predaleč, predaleč: krogla bo preletela cilj za", prevec, "metrov") elif razdalja < zelena_razdalja: premalo = razdalja - zelena_razdalja print("Preblizu, preblizu: krogla bo letela", premalo, "metrov premalo") else: print("Bum.") print("No, kar je, je.")<%10%11%></p> <p>Č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).</p> <p>Po izkušnjah je <code>elif</code> za študente huda, nenavadna skušnjava: nekateri ga pogosto uporabljajo namesto <code>else</code>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 <code>razdalja</code> ni nikoli <em>enaka</em> <code>zelena_razdalja</code>): <xmp class="brush: python">if razdalja > zelena_razdalja: print("Predaleč, predaleč") else: print("Preblizu, preblizu")

Mnogi bi (iz meni neznanega razloga) naredili takole:

if razdalja > zelena_razdalja: print("Predaleč, predaleč") elif razdalja < zelena_razdalja: print("Preblizu, preblizu")<%10%11%></p> <p>Program sicer deluje, vendar je čisto po nepotrebnem prezapleten: drugi pogoj je nepotreben. Če <code>razdalja</code> ni manjša, je pač večja od <code>zelena_razdalja</code> (ob predpostavki, seveda, da ni enaka). <p>Torej: ne zamenjujte <code>else</code> in <code>elif</code>.</p> <h2>Pogoji</h2> <p>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 <code>int</code> ali <code>float</code>) in niso nizi, torej so nekaj četrtega. Podatkovni tip, ki hrani logične vrednosti, se imenuje <code>bool</code> (iz Boolean, po slovensko Booleov). Medtem ko je različnih števil (<code>int</code>, <code>float</code>) 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 <code>bool</code> lahko le eno od dveh vrednosti, <code>True</code> in <code>False</code>. Kot smo mimogrede opazili prejšnji teden, sta <code>True</code> in <code>False</code> 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!) <p>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?</p> <p>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.</p> <p>Za logične izraze so nam na voljo trije operatorji: <code>or</code>, <code>and</code> in <code>not</code>. Kaj pomenijo, je menda jasno, če je kdo v dvomih, pa naj se pouči ob <a href="http://ucilnica1011.fri.uni-lj.si/course/view.php?id=36">prvem predavanju iz Diskretnih struktur</a>.</p> <xmp class="brush: python"> >>> True and True True >>> True and False False >>> False or True True >>> not False True >>> False or not False True

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

>>> 352 > 0 True >>> 5 < 3 False >>> 352 > 0 or 5 < 3 True >>> 352 > 0 or 5 > 3 True >>> 352 > 0 and 5 > 3 True >>> 352 > 0 and 5 < 3 False >>> 352 > 0 and not 5 < 3 True >>> 10 > 8 or 352 > 0 and 5 < 3 True >>> 10 > 8 or (352 > 0 and 5 < 3) True >>> (10 > 8 or 352 > 0) and 5 < 3 False >>> (10 > 8 or 352 > 0) and not 5 < 3 True <%10%11%> <p>Operatorja &gt; in &lt; 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 <code>&gt;=</code> in <code>&lt;=</code>.</p> <p>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, <code>==</code>. Enojni enačaj je dobil svojo zadolžitev prejšnjo uro, namen je namreč prirejanju. Ali sta dve stvari različni, preverimo z <code>!=</code>.</p> <xmp class="brush: python">>>> 1+1 == 2 True >>> 1+1 != 2 False

Ne zamenjujte dvojni in enojnih enačajev. Če pomotoma napišemo (a to se nam bo rekdo zgodilo),

a == 1 + 1 s tem nismo priredili a-ju dvojke, temveč smo le preverili, ali je enak 2. Pogostejša napaka pa bo if a = 1: print("Vrednost a je ena") Ta program pa je sintaktično nepravilen, saj Python za 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:

razlika = razdalja - zelena_razdalja if -0.0001 < razlika < 0.0001: print("Bum") elif razlika > 0: print("Predaleč") else: print("Preblizu")

Č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

if abs(razdalja - zelena_razdalja) < 0.0001: print("Bum.")<%10%11%></p> <h2>Kurzschluss</h2> <p>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.</p> <p>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, (<a href="http://de.wikipedia.org/wiki/Elektrischer_Kurzschluss">kurzschluss</a> 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.</p> <p>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 <code>(10 &lt; 8 or 352 &lt; 0) and 5 &lt; 3</code>. 10 ni manj od 8 in 352 ni manj od 0. <code>False or False</code> je <code>False</code>. Prvi del izraza, vse, kar je pred <code>and</code>-om je <code>False</code>. 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 <code>False and 5 &lt; 3</code>. 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 <code>False</code>.</p> <p>Pravilo je torej takšno: če je izraz, ki je levo od <code>and</code>, neresničen, vrednosti izraza, ki je desno od <code>and</code>, sploh ne računamo.</p> <p>Podobno, le ravno obratno, bližnjico lahko uberemo pri <code>or</code>. Kako bi računali vrednost <code>10 &gt; 8 or 352 &gt; 0</code>? 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 <code>10 &lt; 8 or 352 &gt; 0</code>. 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 <code>10 &lt; 8 <b>and</b> 352 &gt; 0</code> vrgli puško v koruzo že po prvem neuspehu, saj nas morebitni drugi uspeh ne more rešiti.</p> <p>Pravilo kratkega stika si lahko zapomnimo na dva načina. Prvi, formalni, je tale: če je izraz levo od <code>or</code> resničen, potem onega desno od <code>or</code> ne preverjamo, saj že vemo, da bo rezultat resničen. Prav tako, če je izraz levo od <code>and</code> 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.</p> <p>Š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.</p> <xmp class="brush: python"> psov = int(input('Koliko je psov?')) mack = int(input('Koliko je mačk?')) if psov / mack >= 2: print('Psov je veliko več kot mačk') elif mack / psov >= 2: print('Mačk je veliko več kot psov')

Najprej opazka: na tem mestu je prav, da smo uporabili elif. Če bi rekli preprosto

if psov / mack >= 2: print('Psov je veliko več kot mack') else: print('Mačk je veliko več kot psov') to ne bi bilo najboljše. Zakaj ne? Recimo, da bi imeli 10 psov in 8 mačk. Pogoj v prvem 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.

Traceback (most recent call last): File "C:\D\macke-psi.py", line 3, in <module> if psov / mack >= 2: ZeroDivisionError: integer division or modulo by zero

Deljenje z 0. Da se temu izognemo, lahko napišemo takole:

psov = int(input('Koliko je psov?')) mack = int(input('Koliko je mačk?')) if mack > 0: if psov / mack >= 2: print('Psov je veliko vec kot mačk')

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

psov = int(input('Koliko je psov?')) mack = int(input('Koliko je mačk?')) if mack > 0: if psov / mack >= 2: print('Psov je veliko vec kot mačk') elif psov > 0: print('Psov je veliko več kot mačk') if psov > 0: if mack / psov >= 2: print('Mačk je veliko vec kot psov') elif mack > 0: print('Mačk je veliko več kot psov')

Grozno. Ampak pravilno. Veliko lepše pa je takole:

psov = int(input('Koliko je psov?')) mack = int(input('Koliko je mačk?')) if psov > 0 and (mack == 0 or psov / mack >= 2): print('Psov je veliko več kot mačk') if mack > 0 and (mack == 0 or mack / psov >= 2): print('Mack je veliko več kot psov')

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:

psov = int(input('Koliko je psov?')) mack = int(input('Koliko je mačk?')) if psov > 0 and psov >= 2*mack: print('Psov je veliko vec kot mačk') if mack > 0 mack >= 2*psov: print('Mačk je veliko več kot psov')

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.

>>> 0 or 1 1 >>> 0 and 0 0

Prav tako veljajo za neresnične None (None je malo podoben Cjevskumu NULLu, 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:

>>> 1 and 1 1 >>> 1 and 2 2 >>> 2 and 1 1 >>> 2 and 0 0 >>> 0 and 2 0 >>> "" and 2 '' >>> 3 or 0 3 >>> 3 or "" 3 >>> 2 and 3 or 4 3 >>> 2 and 3 or 0 3 >>> 2 and 3 or 4 3

Znate razložiti, kako se to pravzaprav računa? Znate? Prav. Pa se vam zdi uporabno? Ne? Pa poglejte tole:

>>> '' or '<neregistriran uporabnik>' '<neregistriran uporabnik>' >>> 'Janez' or '<neregistriran uporabnik>' 'Janez'

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

x = 1 while x <= 10: print(x) x = x + 1 print('Pa sva preštela')<%10%11%> <p>Zanka <code>while</code> je po svoje podobna stavku <code>if</code>. Medtem ko se stavki znotraj <code><b>if</b></code> izvedejo, <b>če</b> je pogoj resničen, se stavki znotraj <code><b>while</b></code> ponavljajo <b>dokler</b> je pogoj resničen. "If" je "če" in "while" je "dokler", to vedo celo učiteljice angleščine. (Mimogrede, če že štejemo, <code>while</code> je zadnja, enajsta rezervirana beseda danes. Smo že na tretjini!) Gornji program lahko torej kar dobesedno prevedemo iz Pythona v slovenščino: <xmp> postavi x na 1 dokler je x manjši ali enak 10: izpiši vrednost x povečaj x za 1 izpisi 'Pa sva preštela'

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

from math import * g = 10 v = float(input("Vnesi hitrost (m/s)")) zelena_razdalja = float(input("Vnesi zeleno razdaljo (m)")) kot = float(input("Vnesi kot (v stopinjah)")) kot_rad = kot * 2*pi/360 razdalja = v**2 * sin(2*kot_rad) / g while abs(zelena_razdalja - razdalja) > 0.001: print("Krogla bo letela", razdalja, "metrov") if razdalja > zelena_razdalja: print("Predaleč, predaleč") else: print("Preblizu, preblizu") kot = input("Vnesi kot (v stopinjah)") kot_rad = kot * 2*pi/360 razdalja = v**2 * sin(2*kot_rad) / g print("Bum.")

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.

from math import * g = 10 v = float(input("Vnesi hitrost (m/s)")) zelena_razdalja = float(input("Vnesi zeleno razdaljo (m)")) razdalja = -1 while abs(zelena_razdalja - razdalja) > 0.001: kot = float(input("Vnesi kot (v stopinjah)")) kot_rad = kot * 2*pi/360 razdalja = v**2 * sin(2*kot_rad) / g print("Krogla bo letela", razdalja, "metrov") if razdalja > zelena_razdalja: print("Predaleč, predaleč") else: print("Preblizu, preblizu") print("Bum.")

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.

while input("Geslo") != "asdf": print("Poskusi se enkrat!") from math import * g = 10 v = float(input("Vnesi hitrost (m/s)")) ... in tako naprej.

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.

poskusov = 0 while poskusov < 3 and input("Geslo") != "asdf": poskusov += 1 if poskusov == 3: print("Nic ne bo.") else: g = 10 v = float(input("Vnesi hitrost (m/s)")) zelena_razdalja = float(input("Vnesi zeleno razdaljo (m)")) razdalja = -1 while abs(zelena_razdalja - razdalja) > 0.001: kot = float(input("Vnesi kot (v stopinjah)")) kot_rad = kot * 2*pi/360 razdalja = v**2 * sin(2*kot_rad) / g print("Krogla bo letela", razdalja, "metrov") if razdalja > zelena_razdalja: print("Predaleč, predaleč") else: print("Preblizu, preblizu") print("Bum.")

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.

Zadnja sprememba: ponedeljek, 17. september 2012, 17.20