summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Možina <martin.mozina@fri.uni-lj.si>2016-10-17 12:31:13 +0200
committerMartin Možina <martin.mozina@fri.uni-lj.si>2016-10-17 12:31:13 +0200
commit6dbe02d844b81d455dda3086fc0f212f7abdca06 (patch)
tree44681eafeb49a26d174c6791eee575b16cc0a6b3
parent29bc34810cb1d1fbd208f6b5f2dad73188ca7f12 (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.html1276
-rw-r--r--python/problems/lists_and_for/sl.py5
-rw-r--r--python/problems/recursion/find_sum/common.py75
-rw-r--r--python/problems/recursion/find_sum/en.py13
-rw-r--r--python/problems/recursion/find_sum/find_sum.py19
-rw-r--r--python/problems/recursion/find_sum/sl.py28
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>
+''']
+}