diff options
author | Martin Možina <martin.mozina@fri.uni-lj.si> | 2016-10-17 12:31:13 +0200 |
---|---|---|
committer | Martin Možina <martin.mozina@fri.uni-lj.si> | 2016-10-17 12:31:13 +0200 |
commit | 6dbe02d844b81d455dda3086fc0f212f7abdca06 (patch) | |
tree | 44681eafeb49a26d174c6791eee575b16cc0a6b3 | |
parent | 29bc34810cb1d1fbd208f6b5f2dad73188ca7f12 (diff) |
Added an exercise on recursion.
Added lecture notes for lists and for loop.
-rw-r--r-- | python/problems/lists_and_for/list_for_sl.html | 1276 | ||||
-rw-r--r-- | python/problems/lists_and_for/sl.py | 5 | ||||
-rw-r--r-- | python/problems/recursion/find_sum/common.py | 75 | ||||
-rw-r--r-- | python/problems/recursion/find_sum/en.py | 13 | ||||
-rw-r--r-- | python/problems/recursion/find_sum/find_sum.py | 19 | ||||
-rw-r--r-- | python/problems/recursion/find_sum/sl.py | 28 |
6 files changed, 1414 insertions, 2 deletions
diff --git a/python/problems/lists_and_for/list_for_sl.html b/python/problems/lists_and_for/list_for_sl.html new file mode 100644 index 0000000..ae5b385 --- /dev/null +++ b/python/problems/lists_and_for/list_for_sl.html @@ -0,0 +1,1276 @@ +<!DOCTYPE html> +<html lang="sl"> +<head> + <meta charset="utf-8" /> + <title></title> + <link rel="stylesheet" type="text/css" href="/css/codeq.css" /> + <link rel="stylesheet" type="text/css" href="../../style.css" /> +</head> +<body> + +<h2>Seznami</h2> + +<p>Recimo, da smo zbrali teže šestih študentov. Seznam tež zapišemo takole:</p> + +<pre>teze = [74, 82, 58, 66, 61, 84]</pre> + +<p>Seznam (angl. <em>list</em>) + je zaporedje česarkoli, recimo števil, lahko pa tudi česa drugega. Števila + ali kaj drugega naštejemo, vmes pišemo vejice in vse skupaj zapremo v + oglate oklepaje <code>[</code> in <code>]</code>. Za primer sestavimo še + seznam imen študentov:</p> + +<pre>imena = ["Anze", "Benjamin", "Cilka", "Dani", "Eva", "Franc"]</pre> + +<p>in seznam, ki bo povedal, ali gre za študenta ali študentko</p> + +<pre>studentka = [False, False, True, False, True, False]</pre> + +<p>Seznami lahko vsebujejo tudi še hujšo eksotiko. Imamo lahko, recimo, seznam + seznamov - vanj bomo, prikladno, stlačili (pod)sezname teža-ime-spol:</p> + +<pre>podatki = [ + [74, "Anze", False], + [82, "Benjamin", False], + [58, "Cilka", True], + [66, "Dani", False], + [61, "Eva", True], + [84, "Franc", False], + ]</pre> + +<p>To sicer navadno delamo malo drugače (pogosto celo precej drugače), a dokler + tega še ne znamo, bo dobro tudi tako. Mimogrede opazimo še dve stvari: + seznam lahko, če želimo, raztegnemo v več vrst, preprosto tako, da ne + zapremo oklepaja. Naredili bi lahko tudi tole</p> + +<pre>teze = [74, 82, 58, + 66, 61, 84]</pre> + +<p>Seznami lahko vsebujejo tudi še hujšo hujšo eksotiko. V resnici lahko + vsebujejo karkoli, celo, recimo, funkcije:</p> + +<pre>from math import * + +par_funkcij = [sin, cos, radians]</pre> + +<p>Čemu bi kdo hotel narediti seznam funkcij?! I, zato da jih bo klical, + seveda. Pa kdaj kdo kliče funkcije s seznama?! Da, včasih pride prav.</p> + +<p>Nikjer tudi ne piše, da morajo biti vsi elementi seznama istega tipa. To smo + pravzaprav že izkoristili: podseznami s težo, imenom in spolom so vsebovali + število, niz in logično vrednost. Navrgli bi lahko še dve funkciji in en + seznam; pa ne bomo, to se ne dela. Če želimo sezname različnih tipov, + raje uporabimo terke. Kaj so terke, bomo izvedeli vsak čas.)</p> + +<p>Seznam je lahko tudi prazen</p> + +<pre>prazen = []</pre> + +<p>ali pa ima en sam element</p> + +<pre>samo_edini = [42]</pre> + +<p>Ali pa je poln praznih seznamov</p> + +<pre>polnoprazen = [[], [], [], [], [], []]</pre> + + + +<h2>Terka</h2> + +<p>Terka (angl. <em>tuple</em>) je podobna seznamu. Kakšna je pomembna razlika, + bomo še videli, manj pomembna pa je ta, da pri terki namesto oglatih + oklepajev uporabljamo kar okrogle. (Skoraj) vse ostalo ostane enako:</p> + +<pre>teze = (74, 82, 58, 66, 61, 84) +imena = ("Anze", "Benjamin", "Cilka", "Dani", "Eva", "Franc")</pre> + +<p>Pri terkah se nam - zaradi tega, kje in kako jih uporabljamo, pogosteje + zgodi, da vsebuje elemente različnih tipov. Recimo takole:</p> + +<pre>student = (74, "Anze", False)</pre> + +<p>Tudi seznam teža-ime-spol bi raje naredil takole:</p> + +<pre>podatki = [ + (74, "Anze", False), + (82, "Benjamin", False), + (58, "Cilka", True), + (66, "Dani", False), + (61, "Eva", True), + (84, "Franc", False), + ]</pre> + +<p>Tako kot seznami so tudi terke lahko prazne (<code>()</code>) ali pa imajo + po en sam element. Tu imamo težavo pri terki z enim samim elementom: Če bi + napisali <code>(42)</code> to ni seznam, temveč le 42 v oklepaju (odvečnem, + seveda). Da bi povedali, da gre za terko, moramo dodati še vejico:</p> + +<pre>samo_en = (42, )</pre> + +<p>Terko smemo, zanimivo, pisati tudi brez oklepajev.</p> + +<pre>>>> t = 1, 2, 3, 4 +>>> t +(1, 2, 3, 4)</pre> + +<p>Ker to ni preveč pregledno, pa to počnemo le v posebnih primerih, na katere +bomo sproti opozorili.</p> + + +<h3>Razpakiranje v elemente</h3> + +<p>Včasih imamo seznam ali terko z nekaj elementi, ki bi jih radi priredili + več spremenljivkam. Recimo, da imamo</p> + +<pre>student = (74, "Anze", False)</pre> + +<p>Če želimo podatke prirediti spremenljivkam <code>teza</code>, + <code>ime</code> in <code>je_zenska</code>, bi to lahko storili takole:</p> + +<pre>>>> teza, ime, je_zenska = student</pre> + +<p>Doslej smo imeli pri prirejanju na levi strani vedno eno samo spremenljivko, + ki smo ji želeli prirediti vrednost na desni strani enačaja. Tu pa smo na + levi našteli več imen spremenljivk (tri), na desni pa mora biti za to terka + z enakim številom elementov (tremi). Terka? No, lahko je tudi seznam ali + niz.</p> + +<pre>>>> a, b, c = [1, 2, 3] +>>> a +1 +>>> a, b, c = "xyz" +>>> a +'x' +>>> b +'y' +>>> c +'z'</pre> + +<p>Več ko boste programirali v Pythonu, bolj boste čutili moč terk. Omenimo, +recimo, da je z njimi mogoče napisati funkcijo, ki ne vrača ene, temveč več +stvari. Vzemimo funkcijo <code>splitext</code>, ki ji damo ime datoteke, +pa vrne njeno osnovno ime in njeno končnico.</p> + +<pre>>>> from os.path import * +>>> film = "Babylon 5 - 3x04 - Passing through Gethsemane.avi" +>>> zac, konc = splitext(film) +>>> zac +'Babylon 5 - 3x04 - Passing through Gethsemane' +>>> konc +'.avi'</pre> + +<p>V resnici funkcija <code>splitext</code> vrne terko,</p> + +<pre>>>> from os.path import * +>>> splitext(film) +('Babylon 5 - 3x04 - Passing through Gethsemane', '.avi')</pre> + +<p>ki pa smo jo kar mimogrede razpakirali v <code>zac</code> in + <code>konc</code>.</p> + +<p>Seveda bi lahko rezultat funkcije priredili tudi eni sami spremenljivki - + v tem primeru bi bila ta spremenljivka terka.</p> + +<pre>>>> t = splitext(film) +>>> t +('Babylon 5 - 3x04 - Passing through Gethsemane', '.avi')</pre> + +<p>In seveda bi lahko terko nato še razpakirali.</p> + +<pre>ime, koncnica = t</pre> + +<p>Kako v Pythonu zamenjamo vrednosti dveh spremenljivk?</p> + +<pre>a = "Alenka" +b = "Tina"</pre> + +<p>Želeli bi, da bi <code>a</code> postal "Tina" in <code>b</code> "Alenka". + Naivnež bi napisal tale nesmisel:</p> + +<pre>a = b +b = a</pre> + +<p>To seveda ne deluje. V prvi vrstici <code>a</code>-ju priredimo + <code>b</code>, se pravi, "Tina", v drugi vrstici pa <code>b</code>-ju + <code>a</code>, ki pa je medtem postal "Tina". Tako sta po tem + <code>a</code> in <code>b</code> enaka "Tina".</p> + +<p>V drugih jezikih se navadno rešimo tako, da vrednost <code>a</code>-ja + spravimo na varno, preden ga povozimo.</p> + +<pre>tmp = a +a = b +b = tmp</pre> + +<p>V Pythonu pa to ni potrebno. Spremenljivki preprosto zapakiramo v terko, ki + jo takoj razpakiramo nazaj, a v obratnem vrstnem redu.</p> + +<pre>a, b = (b, a)</pre> + +<p>V resnici navadno ne pišemo tako, temveč izpustimo oklepaje.</p> + +<pre>a, b = b, a</pre> + +<p>Oklepaje okrog terke izpuščamo preprosto zato, ker je takšno prirejanje + tako pogosto, da ga programer vajen Pythona takoj prepozna. Pri tem tudi + ne razmišljamo o terkah, temveč prirejanje preberemo preprosto tako, da + <code>a</code>-ju priredimo <code>b</code> in <code>b</code>-ju + <code>a</code>.</p> + + +<h2>Zanka for</h2> + +<p>Python ima dve vrsti zank: zanki <code>while</code>, ki jo že poznamo, dela + družbo <code>for</code>.</p> + +<p>Zastavimo si preprosto nalogo (in ne čisto smiselno) nalogo: izpišimo teže + in kvadrate tež vseh študentov v seznamu. Se pravi (po slovensko):</p> + +<pre>za vsako težo s seznama teze stori tole: + izpiši težo in težo na kvadrat</pre> + +<p>V Pythonu pa prav tako, samo brez sklonov:</p> +<pre>for teza in teze: + print(teza, teza ** 2)</pre> + +<p>V zanki <code>for</code> se skriva prirejanje. Zanka najprej + <em>priredi</em> spremenljivki <code>teza</code> prvi element seznama + <code>teze</code>. Nato se izvrši vsa koda bloka znotraj bloka + <code>for</code> - tako, kot se je dogajalo v zanki <code>while</code>. + V naslednji rundi priredi spremenljivki <code>teza</code> drugi element in + spet izvede kodo znotraj bloka, ... ter tako naprej do konca seznama.</p> + +<p>Zdaj pa izpišimo vse teže, ki so večje od 70.</p> + +<pre>for teza in teze: + if teza > 70: + print(teza)</pre> + +<p>Napišimo program, ki pove, kako težak je najlažji študent.</p> + +<pre>najlazji = 1000 +for teza in teze: + if teza < najlazji: + najlazji = teza +print(najlazji)</pre> + +<p>Je potrebno prevesti v slovenščino? Pa dajmo.</p> + +<pre>za začetek naj bo najlažja teža 1000 (gotovo bomo kasneje našli koga lažjega) +za vsako težo iz seznama tež: + če je teža manjša od najmanjše doslej: + najlažja je ta teža +izpiši najmanjšo težo</pre> + +<p>Deluje ta program samo na seznamih števil? Ali pa bi znal poiskati tudi + najmanjši niz? Kako pravzaprav primerjamo nize? Nize primerja, seveda, po + abecedi. In, da, program deluje tudi na nizih, vrne "najmanjši" niz - prvi + niz po abecedi. Le v začetku moramo napisati + <code>najlazji = "ŽŽŽŽŽŽŽŽŽŽŽŽ"</code> namesto <code>najlazji = 1000</code>. + (Tole se da sprogramirati tudi tako, da deluje za nize in za števila. A ne + bomo ubijali začetnikov s prezapletenimi programi.)</p> + +<p>Mimogrede, Python ima že vdelano funkcijo <code>min</code>, ki vrne + najmanjši element seznama.</p> + +<p>Kako bi izračunali vsoto elementov seznama?</p> + +<pre>s = 0 +for teza in teze: + s += teza</pre> + +<p>Pa poprečno vrednost? Za to moramo poznati dolžino seznama. Pove nam jo +funkcija <code>len</code> (okrajšava za <em>length</em>, če ji kot argument +podamo nek seznam).</p> + +<pre>s = 0 +for teza in teze: + s += teza +s /= len(teze)</pre> + +<p>Samo... malo previdni moramo biti. Seznam bi lahko bil tudi prazen. + Dogovorimo se, da bo poprečna vrednost v tem primeru 0. Pravilno delujoč + program bi bil takšen.</p> + +<pre>s = 0 +for teza in teze: + s += teza +if len(teze) > 0: + s /= len(teze)</pre> + +<p>Zanka for ne deluje le na seznamih. Tako, kot gre prek seznamov, lahko + gre tudi prek terk, nizov in še prek mnogih drugih reči. V Pythonu celo za + branje datotek pogosto uporabimo <code>for</code>, kot se bomo kmalu + naučili.</p> + +<pre>>>> ime = "Cilka" +>>> for crka in ime: +... print(crka) +'C' +'i' +'l' +'k' +'a'</pre> + + +<h2>Najdi takšnega, ki...</h2> + +<p>Kako bi ugotovili, ali vsebuje seznam kako sodo število?</p> + +<pre>s = [11, 13, 5, 12, 5, 16, 7] + +imamo_sodo = False +for e in s: + if e % 2 == 0: + imamo_sodo = True +if imamo_sodo: + print("Seznam vsebuje sodo število") +else: + print("Seznam ne vsebuje sodega števila")</pre> + +<p>V začetku si rečemo, da nimamo nobenega sodega števila. Nato gremo prek +vseh števil in čim naletimo na kakega sodega, zabeležimo, da smo, aleluja, +našli sodo število.</p> + +<p>Ne naredite klasične napake. Tole je narobe:</p> + +<pre>s = [11, 13, 5, 12, 5, 16, 7] + +imamo_sodo = False +for e in s: + if e % 2 == 0: + imamo_sodo = True + else: + imamo_sodo = False +if imamo_sodo: + print("Seznam vsebuje sodo število") +else: + print("Seznam ne vsebuje sodega števila")</pre> + +<p>Ta program se bo ob vsakem lihem številu delal, da doslej ni bilo še +nobenega sodega - zaradi <code>imamo_sodo = False</code> bo pozabil, da je +kdajkoli videl kako sodo število. V gornjem seznamu bo, recimo, pregledal +vsa števila, končal bo s 7 in si ob tem rekel <code>imamo_sodo = False</code>. +Rezultat bo tako napačen.</p> + +<p>Kako pa ugotovimo, ali ima seznam <em>sama soda števila</em>?</p> + +<pre>sama_soda = True +for e in s: + if e % 2 != 0: + sama_soda = False +if sama_soda: + print("Seznam vsebuje sama soda število") +else: + print("Seznam ne vsebuje samo sodih števil")</pre> + +<p>Spet bomo naredili podobno napako kot prej, če bomo pisali:</p> + +<pre>s = [11, 13, 5, 12, 5, 16, 72] + +sama_soda = True +for e in s: + if e % 2 != 0: + sama_soda = False + else: + sama_soda = True</pre> + +<p>Program že takoj, ko vidi 1, ugotovi, da niso vsa števila v seznamu soda, +in postavi <code>sama_soda = False</code>. Vendar melje seznam naprej in ob +vsakem koraku znova nastavlja <code>sama_soda</code>. Ko pride do 72, postavi +<code>sama_soda</code> na <code>True</code>. To pa je ravno zadnje število; +<code>sama_soda</code> ostane <code>True</code>... pa smo tam.</p> + + + +<h2>Prekinjanje zank</h2> + +<p>Ko smo prejšnji teden pisali zanko <code>while</code>, smo postavili pogoj, + do kdaj naj se izvaja. Zanka for bo, če se vmes ne pripeti kaj posebnega, + šla vedno od začetka do konca seznama (terke, niza, datoteke...)</p> + +<p>Včasih to ni potrebno. Pravzaprav smo pravkar videli takšen primer: + rezultat gornjega programa je znan že, čim naletimo na prvo liho + število. Torej bi bilo čisto vseeno, če računalnik v tistem trenutku neha + pregledovanje. To mu v resnici lahko naročimo.</p> + +<pre>sama_soda = True +for e in s: + if e % 2 != 0: + sama_soda = False + break +if sama_soda: + print("Seznam vsebuje sama soda število") +else: + print("Seznam ne vsebuje samo sodih števil")</pre> + +<p>Ukaz <code>break</code> pomeni, da želimo prekiniti zanko. Program skoči +"ven" iz zanke in nadaljuje z izvajanjem ukazov, ki sledijo zanki.</p> + +<p>Mimogrede, na podoben način lahko prekinemo tudi <code>while</code>.</p> + +<p>Tule je bil <code>break</code> skoraj bolj zaradi lepšega - da računalnik +ne izgublja časa brez potrebe. Poskusimo napisati program, ki takrat, ko seznam +ni vseboval samo sodih števil, izpiše prvo liho število.</p> + +<pre>sama_soda = True +for e in s: + if e % 2 != 0: + sama_soda = False + liho = e +if sama_soda: + print("Seznam vsebuje samo soda števila") +else: + print("Seznam ne vsebuje samo sodih števil: prvo liho število je", liho)</pre> + +<p>Brez <code>break</code> tole ne deluje: namesto prvega izpiše zadnje liho +število. Rešimo se lahko z dodatnim pogojem:</p> + +<pre>sama_soda = True +for e in s: + if e % 2 != 0 and sama_soda: + sama_soda = False + liho = e +if sama_soda: + print("Seznam vsebuje samo soda števila") +else: + print("Seznam ne vsebuje samo sodih števil: prvo liho število je", liho)</pre> + +<p>S tem, ko smo dodali <code>and sama_soda</code> smo poskrbeli, da se bosta +<code>sama_soda = False</code> in <code>liho = e</code> izvedla le prvič. Ko +bomo postavili <code>sama_soda</code> na <code>False</code>, bomo dosegli, da +pogoj ne bo nikoli nikoli več resničen.</p> + +<p>Namesto dodatnega pogoja lahko napišemo <code>break</code>.</p> + +<pre>sama_soda = True +for e in s: + if e % 2 != 0 and sama_soda: + sama_soda = False + liho = e + break +if sama_soda: + print("Seznam vsebuje samo soda števila") +else: + print("Seznam ne vsebuje samo sodih števil: prvo liho število je", liho)</pre> + +<p>S tem se program lahko pravzaprav še bolj poenostavi. Ko zanko prekinemo, +<code>e</code> ostane, kar je bil. Torej ne potrebujemo več spremenljivke +<code>liho</code>.</p> + +<pre>sama_soda = True +for e in s: + if e % 2 != 0: + sama_soda = False + break +if sama_soda: + print("Seznam vsebuje samo soda števila") +else: + print("Seznam ne vsebuje samo sodih števil: prvo liho število je", e)</pre> + +<h2>Else po zanki</h2> + +<p>Medtem, ko je ukaz <code>break</code> zelo običajna žival v vseh + programskih jezikih, ima Python še eno posebnost, povezano z zankami. V + večini programskih jezikov lahko <code>else</code> uporabimo kot "odgovor" + na <code>if</code>. V Pythonu pa lahko <code>else</code> sledi tudi zanki + <code>for</code> ali <code>while</code>. V pogojnem stavku (<code>if</code>) + se koda v <code>else</code> izvede, če pogoj ni bil resničen. Po zanki se + <code>else</code> izvede, če se zanka <em>ni prekinila</em> zaradi + <code>break</code>. Torej, <code>else</code> se izvede pri zankah, ki so + se iztekle "po naravni poti".</p> + +<p>Oglejmo si še enkrat gornji program. Kar smo počeli v njem, je kar pogosto: + v zanki nekaj iščemo (recimo kako liho število). Če to reč najdemo, nekaj + storimo (ga izpišemo) in prekinemo zanko. Sicer (torej, če ga ne najdemo), + storimo kaj drugega (izpišemo, da ga nismo našli).</p> + +<p>Zgornji odstavek se približno, a ne čisto povsem prilega zadnjemu kosu +programa. V resnici je napisan po spodnjem programu:</p> + +<pre>for e in s: + if e % 2 != 0: + print("Seznam ne vsebuje samo sodih števil: prvo liho število je", e) + break +else: + print("Seznam vsebuje samo soda števila")</pre> + +<p>Se pravi: če najdemo liho število, napišemo, da seznam vsebuje liho število +in prekinemo zanko. Ta del je jasen. Ne spreglejte pa, kje je <code>else</code>: +poravnan je s <code>for</code> ne z <code>if</code>! Ta <code>else</code> +se torej nanaša na <code>for</code>. Kar napišemo v <code>else</code>-u za +<code>for</code>, se zgodi, če se zanka ni prekinila z <code>break</code>om.</p> + +<p>Preden naredimo še kakšen primer, si oglejmo še nekaj drugega.</p> + + +<h2>Štetje z zanko <code>for</code></h2> + +<div style="font-size: 80%"> +<p>Tisti, ki poznajo kak drug, C-ju podoben jezik, so najbrž pričakovali + <a href="http://en.wikipedia.org/wiki/For_loop#Kinds_of_for_loops">nekoliko + drugačno zanko</a>.</p> + +<p>Poglejmo resnici v oči: v C, C++, C#, Javi sta zanka for in while le dva +načina, na katera povemo eno in isto.</p> + +<pre>for(zacetek; pogoj; korak) { + koda; +}</pre> + +<p>je isto kot</p> + +<pre>zacetek; +while(pogoj) { + koda; + korak; +}</pre> + +<p>Če je ideja zanke for v C-ju podobnih jezikih ta, da se z njo šteje, pa + imamo v Pythonu za to nekaj priročnejšega in splošnejšega.</p> +</div> + +<p>V programih pogosto potrebujemo še dva tipa zank. Pogosto bomo želeli nekaj +ponoviti vnaprej predpisano oziroma pred zanko izračunano število ponovitev, recimo +petkrat ali desetkrat. Prav tako bomo pogosto želeli, da program "šteje" od +10 do 20, od 0 do 100 ali kaj takega.</p> + +<p>Za oboje bo poskrbela kar zanka for, ob pomoči priročne funkcije + <code>range</code>.</p> + +<p>Funkcija <code>range</code> je na prvi pogled povsem neimpresivna. Vrača + sezname zaporednih števil.</p> + +<p>V Pythonu 3.0 je ta funkcija narejena boljše kot v različicah pred njim, + vendar je nova oblika za začetnika manj očitna, zato bomo za trenutek + skočili v stari Python 2.7. Kar sledi, v novem Pythonu namreč ne bi dalo + enakih izpisov.</p> + +<pre>>>> range(5, 10) +[5, 6, 7, 8, 9] +>>> range(10, 20) +[10, 11, 12, 13, 14, 15, 16, 17, 18, 19] +>>> range(7) +[0, 1, 2, 3, 4, 5, 6] +>>> range(16) +[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]</pre> + +<p>Funkcija <code>range</code> je vrnila seznam celih števil, ki se začne pri + prvi vrednosti in konča eno pred zadnjo. Koliko elementov ima seznam, + ki ga vrne <code>range(7)</code>? Sedem. Zato + ker vsebuje prvi element in ne zadnjega, in ker se začne z 0.</p> + +<p>Funkcija <code>range</code> je torej narejena praktično, ker se težko zmotimo +glede tega, koliko elementov bo vrnila.</p> + +<p>Lepo se tudi sešteva. <code>range(10, 15) + range(15, 20)</code> so ravno +vsa števila od 10 do 20. Če bi bil <code>range(10, 15)</code> enak +<code>[10, 11, 12, 13, 14, 15]</code>, bi se v +<code>range(10, 15) + range(15, 20)</code> število 15 ponovilo dvakrat.</p> + +<p>Funkcija ima lahko še en, tretji argument. Ta predstavlja korak.</p> + +<pre>>>> range(5, 15, 2) +[5, 7, 9, 11, 13]</pre> + +<p>Korak je lahko tudi negativen.</p> + +<pre>>>> range(10, 0, -1) +[10, 9, 8, 7, 6, 5, 4, 3, 2, 1] +>>> range(10, 0, -3) +[10, 7, 4, 1] +>>> range(10, 0, -5) +[10, 5]</pre> + +<p>Funkcijo <code>range</code> uporabljamo skoraj izključno v zankah + <code>for</code>.</p> + +<p>V prvi zanki <code>while</code>, ki smo jo napisali, smo izpisali števila +od 1 do 10, takole nekako:</p> + +<pre>i = 1 +while i < 11: + print(i) + i += 1</pre> + +<p>Z zanko <code>for</code> je preprostejši.</p> + +<pre>for i in range(1, 11): + print(i)</pre> + +<p>Seveda ostaja odprto vprašanje, zakaj bi si kdo tako želel izpisati števila +od 1 do 10. Štetje je kljub temu uporabno tudi za kaj drugega, ne le za +izpisovanje. +</p> + +<h3>Praštevila</h3> + +<p>Napisati želimo program, ki pove, ali je dano število praštevilo. Za to bomo + preverili vsa števila od 2 do n-1: če katerokoli od njih deli + <code>n</code>, potem <code>n</code> ni praštevilo.</p> + +<pre>n = int(input("Vpiši število")) +je_prastevilo = True +for i in range(2, n): + if n % i == 0: + je_prastevilo = False</pre> + +<p>V začetku predpostavimo, da je število praštevilo + (<code>je_prastevilo = True</code>), če med števili med 2 in n + (<code>for i in range(2, n)</code>) naletimo na njegov delitelj + (<code>if n % i == 0</code>), pa presodimo, da število pač ne more biti + praštevilo (<code>je_prastevilo = False</code>). Mimogrede naj opozorimo + na napako, na katero stalno opozarjamo:</p> + +<pre>n = int(input("Vpiši število")) +je_prastevilo = True +for i in range(2, n): + if n % i == 0: + je_prastevilo = False + else: + je_prastevilo = True # TO JE NAROBE!</pre> + +<p>Ko je število enkrat obsojeno kot sestavljeno, ga ne moremo več "pomilostiti" + nazaj v praštevilo. Če je tako, pa tudi ni potrebe, da bi po tem, ko enkrat + odkrijemo delitelj, sploh še preverjali naslednje potencialne delitelje. + Zdaj pač že vemo: <code>break</code> in <code>else</code> po zanki. + +<pre>n = int(input("Vpiši število")) + +for i in range(2, n): + if n % i == 0: + print(n, "ni praštevilo, saj je deljivo z", i) + break +else: + print(n, "je praštevilo")</pre> + +<h2>Razpakiranje v zanki <code>for</code>code> in še malo telovadbe</h2> + +<p>Nekoč na začetku predavanja smo imeli seznam študentov in njihovih tež:</p> + +<pre>podatki = [ + (74, "Anze", False), + (82, "Benjamin", False), + (58, "Cilka", True), + (66, "Dani", False), + (61, "Eva", True), + (84, "Franc", False), + ]</pre> + +<p>Recimo, da bi radi izpisali imean študentov in njihove teže.</p> + +<p>Če gremo prek seznama z zanko <code>for</code>, bomo dobivali terke. Te +lahko, vemo, razpakiramo.</p> + +<pre>for student in podatki: + teza, ime, spol = student + print(ime, ": ", teza)</pre> + +<p>Gre pa še dosti elegantneje: razpakiranje lahko opravimo kar znotraj +glave zanke, brez (nepotrebne) terke <code>t</code>:</p> + +<pre>for teza, ime, je_zenska in podatki: + print(ime, ": ", teza)</pre> + +<p>Program torej pravi</p> + +<pre>za vsako trojko (teza, ime, je_zenska) iz seznama podatki: + izpisi ime in tezo</pre> + +<p>Lahko pa se tudi malo igramo in izrišemo graf:</p> + +<pre>for teza, ime, je_zenska in podatki: + print(ime + " " + "*"*teza)</pre> + +<p>Skoraj, ampak ne čisto. Vse skupaj bi radi še poravnali. O tem, kako se v + resnici oblikuje izpis, se bomo pogovarjali drugič, danes pa bomo s tem + opravili nekoliko po domače. + Predpostavimo, da so imena dolga največ 15 znakov. Pred vsako ime bomo + dopisali toliko presledkov, da bo skupna dolžina enaka 15. (Mimogrede + bom izdal, da dolžino niza dobimo s funkcijo <code>len</code>.)</p> + +<pre>for teza, ime, je_zenska in podatki: + print(" "*(15 - len(ime)) + ime + " " + "*"*teza)</pre> + + +<h2>Zanka po dveh seznamih</h2> + +<p>Zdaj pa izpišimo imena in teže, pri tem, da se le-ta nahajajo v ločenih + seznamih.</p> + +<pre>teze = [74, 82, 58, 66, 61, 84] +imena = ["Anze", "Benjamin", "Cilka", "Dani", "Eva", "Franc"] +studentka = [False, False, True, False, True, False]</pre> + +<p>Naivno bi se lotili takole</p> + +<pre> +for ime in imena: + print(ime, ":") +</pre> + +<p>in potem obstali, ker na tem mestu ne moremo priti do teže študenta s tem + imenom. Do običajne in zelo zelo zelo napačne rešitve nas pripelje ta + zgrešeni premislek: zanko moramo speljati prek imen in prek tež, torej</p> + +<pre> +for ime in imena: + for teza in teze: + print(ime, ": ", teza) +</pre> + +<p>Rezultat je nepričakovan za vse tiste, ki ga niso pričakovali. Program + namreč izpiše</p> + +<pre>Anze : 74 +Anze : 82 +Anze : 58 +Anze : 66 +Anze : 61 +Anze : 84 +Benjamin : 74 +Benjamin : 82 +Benjamin : 58 +Benjamin : 66 +Benjamin : 61 +Benjamin : 84 +Cilka : 74 +Cilka : 82 +Cilka : 58 +Cilka : 66 +Cilka : 61 +Cilka : 84 +Daniel : 74 +Daniel : 82 +Daniel : 58 +Daniel : 66 +Daniel : 61 +Daniel : 84 +Eva : 74 +Eva : 82 +Eva : 58 +Eva : 66 +Eva : 61 +Eva : 84 +Franc : 74 +Franc : 82 +Franc : 58 +Franc : 66 +Franc : 61 +Franc : 84 +</pre> + +<p>Računalniki imajo to nadležno navado, da vedno naredijo natanko + tisto, kar jim naročimo. In v tem primeru smo mu naročili tole:</p> + +<pre> +za vsako ime na seznamu ime stori tole: + za vsako tezo na seznamu tez stori tole: + izpisi ime in tezo</pre> + +<p>Kdor ne razume, kaj se je zgodilo in zakaj, naj to nujno premisli, da se ne +bo kasneje učil na lastnih napakah.</p> + +<p>Ko bo to opravljeno, bo razumel: potrebujemo samo eno zanko, ki gre + istočasno po obeh seznamih. Aha, takole?</p> + +<pre> +for ime in imena: +for teza in teze: + print(ime, ": ", teza) +</pre> + +<p>Ummm, ne. Ena zanka je ena zanka. To sta dve. Običajni rešitvi tega problema + sta dve. To, ki bi bila večini bližje, bom danes zamolčal, iz vzgojnih + razlogov (kdor jo bo odkril sam, jo pač bo; žal). Funkcija <code>zip</code> + združi dva seznama (ali več seznamov) v seznam terk.</p> + +<pre>>>> zip(teze, imena) +[(74, 'Anze'), (82, 'Benjamin'), (58, 'Cilka'), (66, 'Daniel'), (61, 'Eva'), (84, 'Franc')] +>>> zip(teze, imena, studentka) +[(74, 'Anze', False), (82, 'Benjamin', False), (58, 'Cilka', True), (66, 'Daniel', False), (61, 'Eva', True), (84, 'Franc', False)] +>>> zip(teze, imena, studentka, teze) +[(74, 'Anze', False, 74), (82, 'Benjamin', False, 82), (58, 'Cilka', True, 58), (66, 'Daniel', False, 66), (61, 'Eva', True, 61), (84, 'Franc', False, 84)]</pre> + +<p><b>Opomba</b>: tudi tale izpis je iz starega Pythona. Od verzije 3.0 si + lahko le predstavljamo, da <code>zip</code> dela takole. V resnici naredi + nekaj majčkeno drugačnega (kaj, je za nas ta mesec še prezapleteno), + rezultat pa je isti.</p> + +<p>Zipamo lahko vse, prek česar lahko naženemo zanko for. Torej ne le seznamov, + temveč tudi nize, terke in še kaj.</p> + +<pre>>>> zip("abcd", "ABCD") +[('a', 'A'), ('b', 'B'), ('c', 'C'), ('d', 'D')] +>>> zip("abcd", range(4)) +[('a', 0), ('b', 1), ('c', 2), ('d', 3)]</pre> + +<p>Rešimo torej nalogo: izpišimo imena in teže iz ločenih seznamov.</p> + +<pre> +for ime, teza in zip(imena, teze): + print(ime, ": ", teza) +</pre> + +<p>Za konec pa izpišimo še poprečno težo študentk.</p> + +<pre>skupna_teza = 0 +studentk = 0 +for teza, je_zenska in zip(teze, studentka): + if je_zenska: + skupna_teza += teza + studentk += 1 +print(skupna_teza / (studentk or 1))</pre> + + + +<h2>Dolžina seznama, terke, niza</h2> + +<p>Seznami, terke in naši stari znanci nizi imajo nekaj skupnega. Pravzaprav + veliko skupnega. Razlikujejo se le v podrobnostih. Skupno jim je, recimo, + da imajo vsi trije dolžino. Gornji seznami šestih tež, šestih imen in + šestih indikatorjev spola so dolžine 6. Terka <code>1, 2, 3, 4</code> je + dolžine 4 in niz "Benjamin" je dolžine 8. Dolžine reči, ki imajo dolžino, + nam pove funkcija <code>len</code>. To sem napisal tako imenitno, da bom + moral še enkrat, da bo sploh kdo razumel: funkcija <code>len</code>sprejme + en argument, recimo seznam, terko ali niz in kot rezultat vrne dolžino + tega seznama, terke ali niza.</p> + +<pre>>>> b = 'Benjamin' +>>> len(b) +8 +>>> len(podatki) +6 +>>> len((1, 2, 3)) +3 +>>> len(12) +Traceback (most recent call last): + File "<interactive input>", line 1, in <module> +TypeError: object of type 'int' has no len()</pre></p> + +<p>Morda je koga presenetilo zadnje: številka 12 bi lahko bila dolga 12, ne? + Ali pa 2, ker ima dve števki? Ne. Funkcija <code>len</code> v bistvu pove + <em>število elementov, ki jih vsebuje podani argument</em>. Niz "Benjamin" + vsebuje 8 znakov, seznam <code>podatki</code> vsebuje 6 podseznamov in + terka <code>(1, 2, 3)</code> ima tri elemente. Število 12 pa nima + elementov.</p> + +<p>Še ena prikladna značilnost seznamov (nizov, terk in še česa): prazni + seznami so, tako kot prazni nizi, neresnični. Seznam lahko uporabimo v + pogoju. +<pre>if s: + print("Seznam s ni prazen") +else: + print("Seznam s je prazen")</pre> + + +<h2>Indeksiranje</h2> + +<p>Do elementov seznamov, terk in nizov ne pridemo le z zanko for. Dobimo jih + lahko tudi tako, da preprosto zahtevamo element na tem in tem mestu. + Vzemimo za primer seznam imen. Spomnimo se, kako je videti.</p> + +<pre>>>> imena +['Anze', 'Benjamin', 'Cilka', 'Dani', 'Eva', 'Franc']</pre> + +<p>Spremenljivka <code>imena</code> vsebuje šest elementov. Če hočemo dostopati + do posameznega elementa - recimo izpisati njegovo vrednost - povemo njegov + indeks, "zaporedno številko". Zapišemo jo v oglate oklepaje za imenom + spremenljivke, takole:</p> + +<pre>>>> imena[2] +'Cilka'</pre> + +<p>Se pravi, <code>imena[2]</code> vrne drugi element seznama.</p> + +<p>Emmm, drugi?! Mar ni drugi element Benjamin, ne Cilka? Drži, ampak kakor + šteje python, je drugi element Cilka. Anže je pa ničti. Ne le v pythonu, + skoraj v vseh jezikih štejemo od 0. Prvi element ima <em>indeks</em> 0, + drugi element 1 in tretji 2. Zakaj? Razlogi so tehnični in praktični. + Tehničnega boste razumeli, ko boste (če boste) poslušali predavanja iz Cja. + Tradicionalno indeks pomeni "odmik od začetka": ko je odmik 0, dobimo prvi + element in ko je odmik 2, se odmaknemo dva elementa, torej pristanemo na + tretjem. Medtem ko je v Cju (in še nižjih jezikih) ta, tehnični, argument + morda smiseln, upravičimo štetje od ničle v Pythonu in drugih višjih + jezikih s praktičnostjo: reči se tako lepše izidejo. Boste videli.</p> + +<p>Če je koga zmedlo, povejmo na glas: oglati oklepaji imajo dve vlogi. Prej + smo jih uporabljali, da smo vanje zaprli seznam, zdaj vanje zapiramo + indekse. Naj vas to ne vznemirja, python bo že pravilno razumel, kaj + mislite, celo v tako hecnih situacijah, kot je tale:</p> + +<pre>>>> [3, 1, 4, 1, 5, 9][2] +4</pre> + +<p>Prvi oklepaji definirajo seznam, drugi zaprejo indeks, 2, ki pove, kateri + element tega seznama nas zanima.<br/> +[<br/> +Zveni bedasto in neuporabno? Potem mi povejte, ali je bedasto in neuporabno + tole: +<pre>["moski", "zenska"][spol[0]]</pre> +in tole +<pre>"MZ"[spol[0]]</pre> +] +</p> + +<p>Na enak način kot sezname indeksiramo tudi terke in nize.</p> +<pre>>>> 'Benjamin'[0] +'B' +>>> 'Benjamin'[2] +'n'</pre> + +<p>Kadar seznam vsebuje sezname, bomo včasih uporabljali dvojne indekse. + Spomnimo se seznama <code>podatki</code>: vsebuje šest elementov, in ti + elementi so spet seznami s po tremi elementi, ki predstavljajo težo, ime + in spol. Vzemimo, recimo, drugi element:</p> + +<pre>>>> en_student = podatki[2] +>>> en_student +[58, "Cilka", True]</pre> + +<p>Zdaj lahko o Cilki nekaj povemo.</p> + +<pre>>>> print(en_student[1], "tehta", en_student[0], "kilogramov.") +Cilka tehta 58 kilogramov.</pre> + +<p>Lahko pa opravimo oboje v enem zamahu.</p> + +<pre>>>> print(podatki[2][1], "tehta", podatki[2][0], "kilogramov.")</pre> + +<p><code>podatki[2][0]</code> pomeni (če preberemo z desne) ničti element + drugega elementa seznama <code>podatki</code>. Če se vam zdi to hudo, vas + lahko potolažim, da ni: v resnici je še bolj grozno. Ker je + <code>podatki[2][1]</code> niz, se lahko vprašamo po, recimo, tretji črki + tega niza. + +<pre>>>> print("Tretja crka niza", podatki[2][1], "je", podatki[2][1][3])</pre> + +<p>(V kost za glodanje C-jašem pa naj vprašam, ali + <code>podatki[2][1][3][0][0][0][0]</code> kaj izpiše ali se pritoži, da je + tu nekaj narobe. Ampak to je pa <em>res</em> neuporabno.)</p> + +<p>Kaj se zgodi, če je indeks prevelik? Nič lepega.</p> + +<pre>>>> "Benjamin"[100] +Traceback (most recent call last): + File "<interactive input>", line 1, in <module> +IndexError: string index out of range</pre> + +<p>Kako velik pa je lahko indeks? Če ima niz osem črk in je prva ničta, je + zadnja sedma. Indeks mora biti torej manjši od dolžine - največji dovoljeni + indeks je tisto, kar vrne funkcija <code>len</code>, minus 1.</p> + +<p>Python (in še marsikateri današnji jezik) ima še en trik: indeksiranje s + konca: -1 je zadnji element, -2 predzadnji in tako naprej.</p> + +<pre>>>> 'Benjamin'[-1] +'n' +>>> 'Benjamin'[-2] +'i' +>>> 'Benjamin'[-3] +'m'</pre> + + +<h2>Rezanje</h2> + +<p>Poleg indeksiranja, ki vrača elemente nizov, seznamov, terk (in še česa), + pozna Python še rezanje (<em>slice</em>), ki vrača dele nizov, seznamov, + terk (in še česa). Rezino opišemo z indeksom prvega elementa in indeksom + prvega elementa, ki ga ne želimo več vključiti v rezino. Med indeksa + postavimo dvopičje. Se pravi, rezina 2:5 pomeni vse elemente od onega z + indeksom 2 do tistega z indeksom 4 (ne 5!).</p> + +<p>Smo to že kje videli? Smo, seveda. Funckija <code>range</code> uporablja +natančno isto logiko. Tako kot pri <code>range</code>, je tudi pri rezanju +to odlična ideja.</p> + +<p>Oglejmo si, kaj na to pravi Benjamin.</p> +<pre>>>> b[2:5] +'nja' +>>> b[5:8] +'min' +>>> b[2:5]+b[5:8] +'njamin'</pre> + +<p>Spodnjo ali zgornjo mejo smemo tudi izpustiti. V tem primeru dobimo vse + elemente od začetka oz. do konca. Tu bosta izkazali svojo moč prav obe + navidez neintuitivni pravili - štetje od 0 in to, da rezina ne vključuje + zadnjega elementa.</p> + +<pre>>>> b[:5] +>>> b[:5] +'Benja' +>>> b[5:] +'min'</pre> + +<p><code>b[:5]</code> vrne prvih pet elementov. <code>b[5:]</code> vrne vse + od petega naprej; ker štejemo od 0, to pomeni, da izpustimo prvih pet. Se + pravi, če želimo nizu <code>s</code> odbiti prvih pet znakov, bomo rekli</p> + +<pre>s = s[5:]</pre> + +<p>Da peti študent vstane le enkrat, nam pride prav, če želimo v niz kaj + vriniti. Če bi radi za petim znakom niza vrinili X, to storimo takole:</p> + +<pre>>>> b[:5]+"X"+b[5:] +'BenjaXmin'</pre> + +<p>Ste opazili, da nam sploh ni bilo treba pomisliti na to, da štejemo od 0? + Vidite, kako naravno je to. Če bi šteli od 1, bi bilo tole precej bolj + zapleteno.</p> + +<p>Še dodatne možnosti prinese indeksiranje od zadaj. Iz niza lahko poberemo, + recimo, elemente od predpredpredzadnjega (-5) do predzadnjega (-2). Koliko + jih bo? Trije, seveda.</p> + +<pre>>>> b[-5:-2] +'jam'</pre> + +<p>No, tegale najbrž ne uporabimo velikokrat. Pač pa nas pogosto zanimajo, + recimo, zadnji štirje znaki. Ali pa vsi razen zadnjih štirih.</p> + +<pre>>>> film = "Babylon 5 - 3x04 - Passing through Gethsemane.avi" +>>> film[-4:] +'.avi' +>>> film[:-4] +'Babylon 5 - 3x04 - Passing through Gethsemane'</pre> + +<p>Mimogrede, tole sicer varneje delamo s funkcijo, ki smo jo že omenili, + namreč <code>splitext</code>, ki prejme kot argument ime datoteke in vrne + terko z dvema elementoma, imenom datoteke brez končnice in končnico.</p> + +<p>Kako pa bi od niza odbili prve tri in zadnja dva znaka? Takole:</p> + +<pre>>>> b[3:-2] +'jam'</pre> + +<p>Že tale primer pokaže, zakaj je štetje od 0 in čudno pravilo, po katerem je + prvi element rezine vključen, zadnji pa ne, tako smiselno in uporabno. + Tisti, ki bi v vsej stvari vendarle videli še kako logiko, pa bo morda + prepričala spodnja tabelica, ob kateri lahko še enkrat premislijo vse + primere.</p> + +<table> + <tr> + <td>0</td><td/><td>1</td><td/><td>2</td><td/><td>3</td><td/><td>4</td><td/><td>5</td><td/><td>6</td><td/><td>7</td><td/><td>8</td><td/> + </tr> + <tr> + <td></td><td>B</td><td/><td>e</td><td/><td>n</td><td/><td>j</td><td/><td>a</td><td/><td>m</td><td/><td>i</td><td/><td>n</td></td> + </tr> + <tr> + <td>-8</td><td/><td>-7</td><td/><td>-6</td><td/><td>-5</td><td/><td>-4</td><td/><td>-3</td><td/><td>-2</td><td/><td>-1</td><td/> + </tr> +</table> + +<p>Vsi znaki med drugim in petim so 'nja', znaki od 3 do -2 so 'jam'... In tako + naprej.</p> + +<p>Vendar še nismo končali. Izpustimo lahko tudi zgornjo in spodnjo mejo. Kaj + dobimo v tem primeru? Cel niz. Je to uporabno? Na nizih pravzaprav ne. Pri + čem drugem pa nam bo prišlo še prav.</p> + +<p>Poleg meja rezine lahko podamo tudi <em>korak</em>. Namesto vsakega znaka + lahko zahtevamo, recimo, vsak drugi znak, tako da dodamo še eno dvopičje, + ki mu sledi velikost koraka.</p> + +<pre>>>> '0123456789'[2:9] +'2345678' +>>> '0123456789'[2:9:2] +'2468' +>>> '0123456789'[2:9:3] +'258' +>>> '0123456789'[2:9:4] +'26'</pre> + +<p>Korak je lahko, tako kot pri <code>range</code> tudi negativen! V tem + primeru je potrebno zamenjati meji - prva mora biti višja od druge.</p> + +<pre>>>> '0123456789'[9:2:-1] +'9876543' +>>> '0123456789'[9:2:-2] +'9753'</pre> + +<p>Meje smemo seveda spet tudi izpuščati:</p> + +<pre>>>> '0123456789'[9::-1] +'9876543210'</pre> + +<p>Takole obrnemo predavatelja.</p> + +<pre>>>> 'demšar janez'[::-1] +'zenaj rašmed'</pre> + +<p>Vse tole je videti nekoliko zapleteno in tuje. In morda je: povaditi bo + treba, pa se bo udomačilo. Pa se splača? Za odbijanje znakov od nizov? Se! + Lepota je v tem, da se natanko enako kot indeksiranje in rezanje nizov + obnaša tudi indeksiranje seznamov in vsega drugega. Pa še kaj - zanke + <code>for</code>, recimo, ki jih bomo vsak čas spoznali.</p> + +<p>Poglejmo si torej še rezanje seznamov.</p> + +<pre>>>> imena = ["Anze", "Benjamin", "Cilka", "Dani", "Eva", "Franc"] +>>> imena[2:5] +['Cilka', 'Dani', 'Eva'] +>>> imena[2:] +['Cilka', 'Dani', 'Eva', 'Franc'] +>>> imena[:2] +['Anze', 'Benjamin'] +>>> imena[:-2] +['Anze', 'Benjamin', 'Cilka', 'Dani'] +>>> imena[-2:] +['Eva', 'Franc'] +>>> imena[::-1] +['Franc', 'Eva', 'Dani', 'Cilka', 'Benjamin', 'Anze']</pre> + +<p><a href="http://www.youtube.com/watch?v=G2eUopy9sd8">Že videno.</a> Rezanje + seznamov se vede enako kot rezanje nizov. Rezanje terk pa prav tako.</p> + + +<h2>Spreminjanje seznamov z indeksiranjem in rezanjem</h2> + +<p>Tole je preprosto. Vsak element seznama se vede na nek način kot + spremenljivka: lahko mu priredimo vrednost in s tem "povozimo" prejšnjo + vrednost.</p> + +<pre>>>> imena +['Anze', 'Benjamin', 'Cilka', 'Dani', 'Eva', 'Franc'] +>>> imena[3] = "Daniel" +>>> imena +['Anze', 'Benjamin', 'Cilka', 'Daniel', 'Eva', 'Franc']</pre> + +<p>Tako kot prej lahko tudi zdaj indeksiramo od spredaj ali od zadaj.</p> + +<p>(Za tiste, ki znate kak drug jezik, predvsem Php: elementov ni mogoče + dodajati s prirejanjem! Pythonovi seznami niso ekvivalentni Phpjevim: temu, + česar se v phpju reče <code>array</code>, je bližji pythonov slovar + (<em>dictionary</em>, <code>dict</code>), ki ga bomo spoznali v + prihodnosti.)</p> + +<p>Pa rezine? Glede na to, da je rezina podseznam, moramo tudi pri prirejanju + rezin prirejati podsezname.</p> + +<pre>>>> imena +['Anze', 'Benjamin', 'Cilka', 'Daniel', 'Eva', 'Franc'] +>>> imena[1:4]=["Ben", "Cecilija", "Dani"] +>>> imena +['Anze', 'Ben', 'Cecilija', 'Dani', 'Eva', 'Franc']</pre> + +<p>Mora biti seznam, ki ga prirejamo, enako dolg kot rezina, ki jo bomo + povozili? Ne, čemu? Takole zamenjamo tri z dvema:</p> + +<pre>>>> imena[1:4]=["Ben-Cecil", "Daniel"] +>>> imena +['Anze', 'Ben-Cecil', 'Daniel', 'Eva', 'Franc'] +</pre> + +<p>Ali pa nobenega s tremi:</p> + +<pre>>>> imena[2:2] = ["D2", "D3", "D4"] +>>> imena +['Anze', 'Ben-Cecil', 'D2', 'D3', 'D4', 'Daniel', 'Eva', 'Franc']</pre> + +<p>Z rezinami lahko tudi pobrišemo del seznama.</p> + +<pre>>>> imena +['Anze', 'Ben-Cecil', 'D2', 'D3', 'D4', 'Daniel', 'Eva', 'Franc'] +>>> imena[2:5] +['D2', 'D3', 'D4'] +>>> imena[2:5]=[] +>>> imena +['Anze', 'Ben-Cecil', 'Daniel', 'Eva', 'Franc']</pre> + +<p>Za brisanje obstaja še veliko drugih načinov, recimo tale</p> +<pre>>>> imena +['Anze', 'Ben-Cecil', 'Daniel', 'Eva', 'Franc'] +>>> del imena[2] +>>> imena +['Anze', 'Ben-Cecil', 'Eva', 'Franc'] +>>> del imena[1:3] +>>> imena +['Anze', 'Franc']</pre> + +<p>Če kdo pričakuje, da bomo zdaj povedali še, da enako delamo tudi z nizi in + terkami ... se moti. Nizov in terk ne moremo spreminjati!</p> + +<pre>>>> b = 'Benjamin' +>>> b[2]='a' +Traceback (most recent call last): + File "<interactive input>", line 1, in <module> +TypeError: 'str' object does not support item assignment</pre> + +<p>Tu je torej osnovna razlika med seznamom in terko: seznam je spremenljiv, + terka ne. Tudi niza ne moremo spreminjati, kakor smo pravkar videli. Kako + pa bi potem zamenjali tretji znak niza b s črko 'a'?</p> + +<pre>>>> b = b[:2]+'a'+b[3:] +>>> b +'Beajamin'</pre> + +<p>To je seveda nerodno. Čemu je torej tako? Čemu ne moremo spreminjati nizov + tako, kot sezname? (In čemu sploh ta trapasta terka?) Videli bomo, da nam + pride včasih zelo zelo prav, da so nekateri objekti nespremenljivi. + (Nekateri - pogosto tudi jaz - pravijo celo, da jeziki sploh ne bi smeli + dopuščati spreminjanja spremenljivk.) Nize pa si v resnici redko želimo + spreminjati - da, pogosto jih bomo sestavljali ali obtesovali, zelo redko + pa si želimo spreminjati posamezne črke. Zato nas to, da so konstantni, ne + bo preveč motilo, velikokrat pa nam bo koristilo.</p> + + +<h2>Računske operacije na seznamih (in drugod)</h2> + +<p>V svojem prvem soočenju s programiranjem smo spoznali aritmetične izraze: + seštevali in množili smo števila, jih kvadrirali in računali sinuse. + Zadnjič smo naleteli na logične izraze, kjer smo računali z logičnimi + vrednostmi, <code>True</code> in <code>False</code>. Danes je čas, da se + nehamo čuditi ob vsakem izrazu posebej in jim dajati pridevke "aritmetični" + "logični" in tako naprej. Računati se da pač z različnimi rečmi in ena od + teh reči so tudi seznami.</p> + +<p>Lahko sezname seštevamo? Kaj dobimo, če seštejemo <code>[2, 5, -1]</code> in + <code>[3, 7, 4]</code>? Dobimo <code>[5, 12, 3]</code> ali + <code>[2, 5, -1, 3, 7, 4]</code>? Oboje bi bilo smiselno, odgovor pa je + takšen: če so se seznami doslej vedli tako podobno nizom, naj se še glede + seštevanja.Ker je <code>'abc' + 'def'</code> enako <code>'abcdef'</code>, + naj bo tudi <code>[2, 5, -1] + [3, 7, 4]</code> enako + <code>[2, 5, -1, 3, 7, 4]</code>.</p> + +<p>Podobno je z množenjem. <code>[2, 5, -1] * 2</code> bi moralo biti po vsej + logiki enako <code>[2, 5, -1] + [2, 5, -1]</code>, se pravi + <code>[2, 5, -1, 2, 5, -1]</code>. In tudi je.</p> + +<p>K seznamu lahko prištejemo le seznam, k nizu niz, k terki terko. Vse tri pa + lahko pomnožimo s celim številom - in z ničemer drugim.</p> + +<p>Poleg <code>+</code> in <code>*</code> pa poznajo vsi trije še dva + operatorja, <code>in</code> in <code>not in</code>. S prvim vprašamo, ali + seznam oz. terka vsebujeta določen element in ali niz vsebuje določen + podniz.</p> + +<pre>>>> 1 in [1, 2, 3] +True +>>> 4 in [1, 2, 3] +False +>>> 'in' in 'Benjamin' +True +>>> 'an' in 'Benjamin'</pre> + +<p>Drugi je seveda ravno nasproten, z njim se vprašamo ali seznam (niz) + <em>ne vsebuje</em> elementa (podniza).</p> + +<pre>>>> 1 not in [1, 2, 3] +False +>>> 4 not in [1, 2, 3] +True +>>> 'in' not in 'Benjamin' +False +>>> 'an' not in 'Benjamin' +True</pre> + +<p>V resnici <code>not in</code> ni prav potreben, saj je + <code>x not in l</code> isto kot <code>not x in l</code>.</p> + + + +</body> +</html> diff --git a/python/problems/lists_and_for/sl.py b/python/problems/lists_and_for/sl.py index 361f1f0..9e306bf 100644 --- a/python/problems/lists_and_for/sl.py +++ b/python/problems/lists_and_for/sl.py @@ -1,2 +1,3 @@ -name = 'Seznami in zanka for' -description = 'Seznami, terke, zanka for.' +name = 'Seznami in zanke' +description = '<a target="_blank" href="[%@resource list_for_sl.html%]">' + \ + 'Seznami in terke, operacije na seznami, zanka <code>for</code></a>.'
\ No newline at end of file diff --git a/python/problems/recursion/find_sum/common.py b/python/problems/recursion/find_sum/common.py new file mode 100644 index 0000000..f61baa3 --- /dev/null +++ b/python/problems/recursion/find_sum/common.py @@ -0,0 +1,75 @@ +import re +from python.util import has_token_sequence, string_almost_equal, \ + string_contains_number, get_tokens, get_numbers, get_exception_desc, \ + all_tokens, has_comprehension, has_loop, almost_equal, get_ast +from server.hints import Hint + +id = 20809 +number = 9 +visible = True + +solution = '''\ +def find_sum(xs, gs): + if gs < 0: + return False + if gs == 0: + return True + if not xs: + return False + return find_sum(xs[1:], gs-xs[0]) or find_sum(xs[1:], gs) +''' + +hint_type = { + 'final_hint': Hint('final_hint') +} + +def test(python, code, aux_code=''): + func_name = 'find_sum' + tokens = get_tokens(code) + if not has_token_sequence(tokens, ['def', func_name]): + return False, [{'id' : 'no_func_name', 'args' : {'func_name' : func_name}}] + + in_out = [ + (([2,7,3,1,4], 10), True), + (([2,3,2,4], 10), False), + (([], 10), False), + (([1,2,3], 2), True), + (([1,2,3], 7), False), + (([2,7,3,1,4], 9), True), + (([2,7,3,2,4], 17), False), + ] + + test_in = [('{0}{1}'.format(func_name, str(l[0])), None) + for l in in_out] + test_out = [l[1] for l in in_out] + + answers = python(code=aux_code+code, inputs=test_in, timeout=1.0) + n_correct = 0 + tin, tout = None, None + for i, (ans, to) in enumerate(zip(answers, test_out)): + res = ans[0] + corr = res == to + n_correct += corr + if not corr: + tin = test_in[i][0] + tout = to + + passed = n_correct == len(test_in) + hints = [{'id': 'test_results', 'args': {'passed': n_correct, 'total': len(test_in)}}] + if tin: + hints.append({'id': 'problematic_test_case', 'args': {'testin': str(tin), 'testout': str(tout)}}) + if passed: + hints.append({'id': 'final_hint'}) + + return passed, hints + + +def hint(python, code, aux_code=''): + tokens = get_tokens(code) + + # run one test first to see if there are any exceptions + answer = python(code=aux_code+code, inputs=[(None, None)], timeout=1.0) + exc = get_exception_desc(answer[0][3]) + if exc: return exc + + return None diff --git a/python/problems/recursion/find_sum/en.py b/python/problems/recursion/find_sum/en.py new file mode 100644 index 0000000..0d1807c --- /dev/null +++ b/python/problems/recursion/find_sum/en.py @@ -0,0 +1,13 @@ +id = 20809 +name = 'Find sum' + +description = '''\ +<p>(translation missing)</p>''' + +hint = { + 'plan': '''\ +<p>(translation missing)</p>''', + + 'no_input_call': '''\ +<p>(translation missing)</p>''', +} diff --git a/python/problems/recursion/find_sum/find_sum.py b/python/problems/recursion/find_sum/find_sum.py new file mode 100644 index 0000000..85f472b --- /dev/null +++ b/python/problems/recursion/find_sum/find_sum.py @@ -0,0 +1,19 @@ +def find_sum(xs, gs): + if gs < 0: + return False + if gs == 0: + return True + if not xs: + return False + return find_sum(xs[1:], gs-xs[0]) or find_sum(xs[1:], gs) + +print (find_sum([2,7,3,1,4], 10)) +print (find_sum([2,3,2,4], 10)) +print (find_sum([], 10)) +print (find_sum([1,2,3], 2)) +print (find_sum([1,2,3], 7)) +print (find_sum([2,7,3,1,4], 9)) +print (find_sum([2,7,3,2,4], 17)) + + + diff --git a/python/problems/recursion/find_sum/sl.py b/python/problems/recursion/find_sum/sl.py new file mode 100644 index 0000000..00aad43 --- /dev/null +++ b/python/problems/recursion/find_sum/sl.py @@ -0,0 +1,28 @@ +import server +mod = server.problems.load_language('python', 'sl') + +id = 20809 +name = 'Išči vsoto' + +description = '''\ +<p> +Napiši funkcijo <code>find_sum(xs, gs)</code>, ki kot argument prejme seznam +pozitivnih števil in število, ki predstavlja ciljno vsoto. Funkcija vrne +<code>True</code>, kadar lahko sestavimo ciljno vsoto iz števil v seznamu. </p> + +<pre> +>>> find_sum([2,7,3,1,4], 10) +True +>>> find_sum([2,3,2,4], 10) +False +</pre> +''' + +plan = [] + +hint = { + 'final_hint': ['''\ +<p>Program je pravilen! <br> +</p> +'''] +} |