summaryrefslogtreecommitdiff
path: root/python/problems/comprehensions/comprehensions_sl.html
diff options
context:
space:
mode:
Diffstat (limited to 'python/problems/comprehensions/comprehensions_sl.html')
-rw-r--r--python/problems/comprehensions/comprehensions_sl.html572
1 files changed, 572 insertions, 0 deletions
diff --git a/python/problems/comprehensions/comprehensions_sl.html b/python/problems/comprehensions/comprehensions_sl.html
new file mode 100644
index 0000000..4ebea98
--- /dev/null
+++ b/python/problems/comprehensions/comprehensions_sl.html
@@ -0,0 +1,572 @@
+<!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>
+
+<p>Matematiki si lahko privoščijo opisati množico dvakratnikov vseh naravnih
+ števil, katerih kvadrat je manjši od 120, takole:
+ \(S=\{\,2\cdot x\mid x \in \mathbb{N},\ x^2<120\,\}\). Mi tudi.</p>
+
+<h2>Seznami</h2>
+
+<p>Vendar pojdimo lepo počasi. Začnimo s tistim, kar najbolj poznamo: s seznami.
+ Doslej smo jih zapisali tako, da smo v oglatih oklepajih naštevali njihove
+ elemente,</p>
+
+<pre>k = [9, 16, 81, 25, 196]</pre>
+
+<p>Oglate oklepaje bomo uporabljali tudi poslej, le da znotraj njih ne bomo
+ našteli elementov, temveč napisali izraz, ki jih sestavi. Recimo, da bi
+ hotel seznam korenov števil iz gornjega seznama (po nekem čednem naključju
+ gornji seznam vsebuje ravno same kvadrate). Naredil bi tako:</p>
+
+<pre>>>> [sqrt(x) for x in k]
+[3.0, 4.0, 9.0, 5.0, 14.0]</pre>
+
+<p>Torej: napisal sem oglate oklepaje, kot vedno, kadar definiram seznam, a
+ namesto, da bi naštel elemente, sem povedal, kakšni naj bodo. Rekel sem,
+ naj bodo elementi mojega novega seznama <em>koren iz x</em>, pri čemer so
+ <code>x</code> elementi seznama <code>k</code>. Kaj pa, če bi hotel
+ namesto tega imeti kvadrate števil? Isti šmorn:</p>
+
+<pre>>>> [x**2 for x in k]
+[81, 256, 6561, 625, 38416]</pre>
+
+<p>Oblika tega, kar pišemo, je vedno
+ <code>[<em>izraz</em> for <em>spremenljivka</em> in <em>nekaj</em>]</code>,
+kjer je <em>spremenljivka</em> ime neke spremenljivke (npr. <code>x</code>,
+<em>izraz</em> nek izraz, ki (običajno, ne pa čisto nujno) vsebuje to
+spremenljivko (npr. <code>sqrt(x)</code> ali <code>2*x</code>, <em>nekaj</em>
+pa je nekaj, čez kar je možno spustiti zanko for, torej seznam, slovar,
+množica, datoteka ali kaj, česar še ne poznamo, vendar bomo spoznali še danes.
+</p>
+
+<p>Imam seznam imen, recimo,</p>
+<pre>l = ["Ana", "Berta", "Cilka", "Dani", "Ema"]</pre>
+
+<p>Kako bi izračunal poprečno dolžino imena? Imam funkcijo <code>sum</code>,
+ ki zna sešteti števila v seznamu. Potrebujem torej le še seznam dolžin
+ (namesto seznama imen). Tega pač preprosto dobim: izračunati želim dolžino
+ niza <code>ime</code> za vsako <code>ime</code> v seznamu
+ <code>imena</code>.</p>
+
+<pre>>>> [len(ime) for ime in imena]
+[3, 5, 5, 4, 3]</pre>
+
+<p>Zdaj pa tole le še seštejem in delim z dolžino seznama.</p>
+
+<pre>>>> sum([len(ime) for ime in imena]) / len(imena)
+4.0</pre>
+
+<p>Poprečna dolžina imena ni nič posebej zanimivega. Računajmo raje poprečno
+težo in, da nam ne bo dolgčas, jo bomo računali iz teh podatkov:</p>
+
+<pre>podatki = [
+ (74, "Anže", False),
+ (82, "Benjamin", False),
+ (58, "Cilka", True),
+ (66, "Dani", False),
+ (61, "Eva", True),
+ (84, "Franc", False),
+]</pre>
+
+<p>Takole nam je storiti: z zanko se zapeljemo čez podatke, izračunamo vsoto
+prvih elementov in jo delimo z dolžino seznama.</p>
+
+<p>Lepo prosim, ne tako:</p>
+
+<pre>vsota = 0
+for i in range(len(podatki)):
+ vsota += podatki[i][0]
+print(vsota / len(podatki))</pre>
+
+<p>Tole je že boljše:</p>
+
+<pre>vsota = 0
+for podatek in podatki:
+ vsota += podatek[0]
+print(vsota / len(podatki))</pre>
+
+<p>In še malo bolje:</p>
+
+<pre>vsota = 0
+for teza, ime, zenska in podatki:
+ vsota += teza
+print(vsota / len(podatki))</pre>
+
+<p>Zdaj pa naredimo tako, kot smo se naučili danes. Naredimo lahko
+
+<pre>vsota = sum([podatek[0] for podatek in podatki])</pre>
+
+<p>ali, po zadnji različici</p>
+
+<pre>vsota = sum([teza for teza, ime, zenska in podatki])</pre>
+
+<p>Pri izpeljevanju seznamov lahko dodamo še pogoj, s katerim izbiramo
+elemente, ki naj se dodajo.</p>
+
+<p>Kako bi sestavili seznam kvadratov vseh lihih
+števil do 100? Seznam kvadratov vseh števil do 100 je trivialen, napisati
+nam je treba le:</p>
+
+<pre>s = [x**2 for x in range(100)]</pre>
+
+<p>Če želimo pobrati samo liha števila, lahko v izpeljavo seznama dodamo še
+pogoj.</p>
+
+<pre>s = [x**2 for x in range(100) if x % 2 == 1]</pre>
+
+<p>Tole pravzaprav ni čisto potrebno, lahko bi rekli preprosto</p>
+
+<pre>s = [x**2 for x in range(1, 100, 2)]</pre>
+
+<p>Kaj pa, če bi hoteli seznam kvadratov vseh števil do 100, ki niso deljiva s
+ 7 in ne vsebujejo števke 7?</p>
+
+<pre>[x**2 for x in range(100) if (x % 7 != 0) and ("7" not in str(x))]</pre>
+
+<p>V pogoju smo zaradi preglednosti uporabili oklepaje, čeprav sicer niso
+potrebni). Prvi del poskrbi, da število ni deljivo s 7 (ostanek po deljenju s
+7 mora biti različni od 0). Drugi del poskrbi, da število ne vsebuje števke 7:
+število preprosto pretvorimo v niz in preverimo, da ne vsebuje sedemke.</p>
+
+<p>Kako bi izračunali vsoto tež moških v gornjem seznamu? Za zanko dodamo
+pogoj. Za začetek sestavimo le seznam imen vseh moških.</p>
+
+<pre>[ime for teza, ime, zenska in podatki if not zenska]</pre>
+
+<p>V definicijo našega seznama lepo na konec, za <code>for</code>, dodamo še
+<code>if</code> in pogoj, ki mu mora zadoščati element, da nas zanima.</p>
+
+<p>Z vsoto je podobno.</p>
+
+<pre>vsota = sum([teza for teza, ime, zenska in podatki if not zenska])</pre>
+
+<p>Zdaj veste, zakaj sem tako utrujal, da ne uporabljajte
+<code>for i in range(len(s))</code>: zato, ker z njim namesto lepega, kratkega
+preglednega <code>[ime for teza, ime, zenska in podatki if not zenska]</code>
+dobimo
+<code>[podatki[i][1] for i in range(len(podatki)) if not podatki[i][2]]</code>.
+Seveda lahko delate tudi na ta, drugi način. Svobodni ljudje ste in noben zakon
+ne prepoveduje mazohizma.</p>
+
+<p>Sestavimo seznam vseh deliteljev danega števila <code>n</code>. Vzemimo,
+ recimo <code>n = 60</code>. Seznam deliteljev je tedaj seznam vseh
+ <code>x</code>, pri čemer <code>x</code> prihaja z intervala
+ 1 &le; x &le; n, za katere velja, da je ostanek po deljenju
+ <code>n</code> z <code>x</code> enak 0 (torej <code>x</code> deli
+ <code>n</code>).</p>
+
+<pre>def delitelji(n):
+ return [x for x in range(1, n+1) if n % x == 0]</pre>
+
+<p>Zdaj lahko hitro ugotovimo, ali je dano število popolno: popolna
+števila so (se še spomnimo?) števila, ki so enaka vsoti svojih
+deliteljev (brez sebe samega).
+
+<pre>def popolno(n):
+ return n == sum(delitelji(n))</pre>
+</p>
+
+<p>Še bolj imenitna števila so praštevila, števila brez deliteljev. Se pravi
+ tista, pri katerih je seznam deliteljev med 2 in tem številom prazen.</p>
+
+<pre>def prastevilo(n):
+ return not delitelji(n)</pre>
+
+<p>Če funkcije <code>delitelji</code> še ne bi imeli - nič hudega. Ali je
+ število praštevilo, bi še vedno dognali v eni sami vrstici.</p>
+
+<pre>def prastevilo(n):
+ return not [x for x in range(2, n) if n % x == 0])</pre>
+
+<p>S to funkcijo hitro sestavimo seznam vseh praštevil med 2 in 100:</p>
+
+<pre>>>> [n for n in range(2, 101) if prastevilo(n)]
+[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]</pre>
+
+<p>Znamo pa, jasno, tudi brez funkcije; tisto, kar dela funkcija, pač kar
+prepišemo v pogoj.</p>
+
+<pre>>>> [n for n in range(2, 101) if not [x for x in range(2, n) if n % x == 0])]
+[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]</pre>
+
+<p>Takšnih reči sicer raje ne pišemo, saj hitro postanejo nepregledne.</p>
+
+<p>Kdor je gledal stare naloge, je opazil nalogo z vislicami. V eni od nalog
+ je bilo potrebno napisati funkcijo <code>pokazi_crke(beseda, crka)</code>,
+ ki kot argument dobi besedo in množico črk, vrniti pa mora besedo, v kateri
+ so "vidne" vse črke, ki so v množici, tiste, ki jih ni, pa je potrebno
+ zamenjati s piko. Tako mora, recimo,
+ <code>pokazi_crke("BESEDA", {"E", "D"})</code> vrniti <code>.E.ED.</code>.
+</p>
+
+<p>Nalogo rešimo lepo po korakih. Vzemimo neko množico črk in neko besedo.</p>
+
+<pre>>>> crke = {"E", "D"}
+>>> beseda = "BESEDA"</pre>
+
+<p>Zdaj razsekajmo besedo na črke.</p>
+
+<pre>>>> [b for b in beseda]
+['B', 'E', 'S', 'E', 'D', 'A']</pre>
+
+<p>Zdaj pa naj seznam vsebuje <code>b</code> le, če je <code>b</code> v množici
+ <code>crke</code>, sicer pa piko.</p>
+
+<pre>>>> [b if b in crke else "." for b in beseda]
+['.', 'E', '.', 'E', 'D', '.']</pre>
+
+<p>Da združimo črke v niz, potrebujemo le še večno zeleni <code>join</code>.</p>
+
+<pre>>>> "".join([b if b in crke else "." for b in beseda])
+'.E.ED.'</pre>
+
+<p>Celotna rešitev naloge je torej</p>
+
+<pre>def pokazi_crke(beseda, crke):
+ print "".join((c if c in crke else ".") for c in beseda)
+</pre>
+
+<p>V splošnem: vsak kos kode, ki izgleda takole</p>
+
+<pre>r = []
+for e in s:
+ if pogoj(e):
+ r.append(funkcija(e))</pre>
+
+<p>lahko prepišemo v</p>
+
+<pre>r = [funkcija(e) for e in s if pogoj(e)]</pre>
+
+<p>Kdor želi še kakšen primer, si bo ogledal
+ <a href="/mod/page/view.php?id=11856">rešitev
+ kronogramov</a> in
+ <a href="/mod/page/view.php?id=11814">napadalnih kraljic</a>.
+</p>
+
+<h2>Množice</h2>
+
+<p>Množice sestavljamo natančno tako kot sezname, le da namesto oglatih
+ oklepajev uporabimo zavite. Takole dobimo vse delitelje 60</p>
+
+<pre>>>> {i for i in range(1, 61) if 60 % i == 0}
+{1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 60, 30}</pre>
+
+
+<h2>Slovarji</h2>
+
+<p>Ista reč. Če hočemo narediti slovar, ki bo kot ključe vseboval števila do
+ 10, kot vrednosti pa njihove kvadrate, napišemo</p>
+
+<pre>{i: i**2 for i in range(11)}</pre>
+
+<h2>Generatorji</h2>
+
+<p>Tule bi pričakovali nekaj drugega: terke. Lahko tako kot sezname, množice in
+ slovarje naredimo tudi terke in nize? Ne. Na takšen način določeni nizi
+ očitno ne bi imeli nobenega smisla, pa tudi terke ne bi bile preveč
+ uporabne. Pač pa nas čaka - in bo pogosto v okroglih oklepajih - še nekaj
+ bolj nenavadnega in imenitnejšega: generatorji.</p>
+
+<pre>>>> g = (x**2 for x in range(10))</pre>
+
+<p>Tale <code>g</code>, kot se hitro prepričamo, ni terka, temveč nekaj
+ drugega.</p>
+
+<pre>>>> g
+<generator object <genexpr> at 0x667bc0></pre>
+
+<p>Kaj je generator, čemu služi in kako ga uporabljamo? Na prvi dve vprašanji
+ lahko odgovorimo naenkrat: generator je nekaj, kar generira objekte.
+ Vsakič, ko bomo od njega zahtevali nov objekt, bo vrnil novo število -
+ najprej 0, potem 1, potem 4, potem 9 ... pač kvadrate naravnih števil do
+ 10. Na tretje vprašanje, kako ga uporabljamo, nam skoraj ni potrebno
+ odgovarjati, saj generatorje (in njim podobne reči) že dolgo uporabljamo.
+ Generatorje najpreprosteje uporabimo z zanko for. Še več, zanka for deluje
+ na tistih rečeh - in samo tistih rečeh - ki se znajo obnašati kot
+ generatorji (torej: v resnici so generatorji ali pa podpirajo osnovne
+ funkcije generatorjev).</p>
+
+<pre>>>> for i in g:
+... print(i, end=" ")
+...
+0 1 4 9 16 25 36 49 64 81</pre>
+
+<p>Če poskusimo ponoviti, ne gre: generator je že "iztrošen". Kar je imel
+ zgenerirati, je zgeneriral.</p>
+
+<pre>>>> for i in g:
+... print(i, end=" ")
+...
+>>></pre>
+
+<p>Generator je torej videti kot izpeljani seznam, le da namesto oglatih
+ oklepajev uporabimo okrogle. Kadar ga pošljemo kot edini argument funkciji,
+ smemo oklepaje celo izpustiti. Napišimo funkcijo, ki kot argument dobi
+ seznam števil in vrne prvo število, ki je večje od 50.</p>
+
+<pre>def nad50(s):
+ for e in s:
+ if e > 50:
+ return e</pre>
+
+<p>Preskusimo jo, trikrat:</p>
+
+<pre>>>> nad50([5, 1, 40, 1, 82, 12, 6])
+82
+>>> nad50([x**2 for x in range(10)])
+64
+>>> nad50(x**2 for x in range(10))
+64</pre>
+
+<p>V prvem primeru je dobila najobičajnejši seznam. Tako smo si predstavljali,
+ ko smo jo pisali. V drugem primeru smo ji dali seznam kvadratov števil do
+ 10 in vrnila je prvi kvadrat, ki je večji od 50. V tretjem klicu pa ji
+ sploh nismo dali seznama, temveč generator, ki je dajal kvadrate -
+ natančno isto reč kot gornji <code>g</code>. Funkcija je čez generator
+ pognala zanko <code>for</code> natančno tako, kot jo sicer poganja čez
+ sezname.</p>
+
+<p>V čem je prednost generatorjev pred seznami? V tem, da ne sestavijo seznama.
+ Če želimo izračunati vsoto kvadratov prvih stotih števil in za to uporabimo
+ generator, na ta način ne sestavimo seznama teh števil (kot bi ga, če bi
+ rekli <code>sum([x**2 for x in range(100)])</code>, temveč števila, namreč
+ kvadrate, generiramo sproti (tako, da pokličemo
+ <code>sum([x**2 for x in range(100)])</code>. Hm, pa to v resnici deluje?
+ No, seveda. Funkcija <code>sum</code> bi lahko bila napisana takole</p>
+
+ <pre>def sum(s):
+ vsota = 0
+ for e in s:
+ vsota += e
+ return vsota</pre>
+
+ <p>Zanka for lahko gre prek generatorja (in v resnici vedno gre prek
+ generatorja), torej ona reč deluje.</p>
+
+ <p>Že, že, poreče pozornejši študent: kaj pa <code>range(100)</code>? Mar ta
+ ne sestavi seznama stotih števil? Smo res kaj dosti pridobili - namesto
+ seznama kvadratov števil do 100 imamo pač števila do 100 - je to res tak
+ napredek? Tu lahko študenta pohvalimo za budnost, vendar se moti.</p>
+
+ <pre>>>> range(100)
+ range(0, 100)</pre>
+
+ <p>V resnici <code>range</code> ne sestavi seznama, temveč generator. Res se
+ nam ni predstavil kot kak "generator object" temveč kot "range(0, 100)",
+ vendar v resnici ni ničesar sestavil. Nobenega seznama s stotimi števili
+ ni. Kako vem? Preprosto: </p>
+
+ <pre>>>> range(1000000000000)
+ range(0, 1000000000000)</pre>
+
+ <p>Če bi <code>range</code> sestavljal sezname, bi bil to seznam z bilijon
+ elementi. Če bi vsak od njih zavzel samo en bajt (pa jih v resnici najbrž
+ vsaj 20, verjetneje pa 32), bi shranjevanje takšnega seznama zahtevalo
+ pomnilnik velik en terabajt.</p>
+
+<p>Tako smo torej generatorje uporabljali že, odkar vemo za <code>range</code>.
+ (Morda se kdo spomni, da sem vam takrat, na onem davnem predavanju, ko sem
+ povedal za funkcijo <code>range</code>, le-to pokazal na Pythonu 2.7? Tako
+ je bilo lažje, saj je takrat še vračal sezname. To je ena od sprememb od
+ različice 3.0 naprej: po novem vrača generatorji.)</p>
+
+<p>Podobno je s slovarji: metode <code>__keys__</code>, <code>__values__</code>
+ in <code>__items__</code> vračajo generatorje, ne seznamov.</p>
+
+<h2>Funkcije, ki generirajo</h2>
+
+<p>Še nekoliko naprednejša snov: Bi znali napisati generator Fibonaccijevih
+ števil, tako kot smo napisali generator kvadratov? Da, vendar bo
+ preprostejša nekoliko drugačna pot. Napisali bomo funkcijo, ki ne vrača le
+ enega rezultata temveč "generira" rezultate. Torej, nekaj takšnega:</p>
+
+<pre>def fibonacci(n):
+ a = b = 1
+ for i in range(n):
+ return a # Tole ne deluje!
+ a, b = b, a+b</pre>
+
+<p>Tale funkcija ne deluje, napisali smo jo le, da ilustriramo idejo: radi bi
+ naredili zanko. Ko funkcijo prvič pokličemo, bi <code>return</code> vrnil
+ prvo Fibonaccijevo število. Ko jo pokličemo naslednjič, se funkcija ne bi
+ izvajala od začetka, temveč od tam, kjer smo nazadnje vrnili rezultat, se
+ pravi z vrstico, ki sledi <code>return</code>u. No, v resnici naredimo
+ natanko tako, le namesto <code>return</code>a moramo uporabiti
+ <code>yield</code>:</p>
+
+<pre>def fibonacci(n):
+ a = b = 1
+ for i in range(n):
+ yield a
+ a, b = b, a+b</pre>
+
+<p>Preskusimo.</p>
+
+<pre>>>> for i in fibonacci(5):
+... print(i)
+1
+1
+2
+3
+5</pre>
+
+<p>Napišemo lahko celo funkcijo, ki vrne (no, generira) <b>vsa</b>
+ Fibonaccijeva števila.</p>
+
+<pre>def fibonacci():
+ a = b = 1
+ while True:
+ yield a
+ a, b = b, a+b</pre>
+
+<p>Neskončno zanko, <code>while True</code>, smo že videli, vendar je bil v
+ njej vedno <code>break</code>, ki jo je nekoč prekinil. Kdo pa prekine to
+ zanko? Če nismo previdni, nihče. Če rečemo</p>
+
+<pre>>>> for i in fibonacci():
+... print(i)</pre>
+
+<p>bo program izpisoval v nedogled. Pač pa lahko poiščemo, recimo, prvo
+ Fibonaccijevo število, ki je večje od 50.</p>
+
+<pre>>>> for i in fibonacci():
+... if i > 50:
+... print(i)
+... break
+55</pre>
+
+<p>Ali pa, ah, saj imamo za to že funkcijo, <code>nad50</code>. Naj kar ta
+pove, katero je prvo Fibonaccijevo število večje od 50!</p>
+
+<pre>>>> nad50(fibonacci())
+55</pre>
+
+<p>V zvezi z vsemi temi rečmi bi bilo mogoče povedati še zelo veliko. Tole je
+ ena najbolj kul tem v Pythonu. Pravzaprav treba priznati, da to niti ni
+ tema iz Pythona. V ozadju tega, kar počnemo tule, je poseben slog
+ programiranja,
+ <a href="https://en.wikipedia.org/wiki/Functional_programming">funkcijsko
+ programiranje</a>. Python ga omogoča in med "normalnimi" jeziki je za
+ takšen slog pravzaprav eden boljših. Obstajajo pa jeziki, ki so posebej
+ narejeni za takšno programiranje. Če je bilo komu tole, kar smo počeli
+ doslej, všeč naj si nujno ogleda kak
+ <a href="https://en.wikipedia.org/wiki/Standard_ML">SML</a> ali
+ <a href="https://en.wikipedia.org/wiki/Haskell_(programming_language)">
+ Haskell</a>, morda pa ga bo zabaval tudi
+ <a href="https://en.wikipedia.org/wiki/Racket_(programming_language)">
+ Racket</a> (dialekt Lispa).</p>
+
+<p>V Pythonu pa si bo ta, ki so mu bile te reči všeč, dobro ogledal module
+<a href="https://docs.python.org/3/library/functools.html">functools</a>,
+<a href="https://docs.python.org/3/library/itertools.html">iterools</a> in
+<a hreF="https://docs.python.org/3/library/operator.html">operator</a>.</p>
+
+<h3>Generator deliteljev</h3>
+
+<p>Še en zanimiv primer je generator, ki vrne vse delitelje podanega števila.
+</p>
+
+<pre>def delitelji(n):
+ for i in range(1, n + 1):
+ if n % i == 0:
+ yield i</pre>
+
+<p>Opazimo lahko, da z enim deliteljem dobimo dva: če je <code>i</code> delitelj
+<code>n</code>-ja, je tudi <code>n // i</code> delitelj <code>n</code>-ja.
+Če je tako, zadošča, da gremo do korena iz <code>n</code> in vrnemo po dva
+delitelja.</p>
+
+<pre>from math import sqrt
+
+def delitelji(n):
+ for i in range(1, int(sqrt(n) + 1)):
+ if n % i == 0:
+ yield i
+ yield n // i</pre>
+
+<p>Koren iz <code>n</code> moramo spremeniti v celo število, ker <code>range</code>
+ne mara necelih.</p>
+
+<p>Pazite, tole sta dva <code>yield</code>a. Funkcija se izvaja tako, ad vrne
+najprej eno število, in ko zahtevamo naslednje, se izvede naslednji
+<code>yield</code>.</p>
+
+<p>Funkcija je zdaj bistveno hitrejša (pri velikih številih bi se to utegnilo
+kar poznati - namesto, da gre do milijona, bo šla le do 1000. Vendar žal ne
+dela pravilno. Če je <code>n</code>, recimo, <code>25</code>, bo funkcija
+dvakrat vrnila 5. A tega se znamo hitro znebiti.</p>
+
+<pre>from math import sqrt
+
+def delitelji(n):
+ for i in range(1, int(sqrt(n) + 1)):
+ if n % i == 0:
+ yield i
+ if i ** 2 != n:
+ yield n // i</pre>
+
+<p>Zdaj nas morda moti le še to, da števila ne prihajajo v pravem vrstnem redu.
+Kot delitelje 42 bi namesto 1, 2, 3, 6, 7, 14, 21, 42 dobili 1, 42, 2, 21, 3,
+14, 6, 7. To lahko popravimo tako, da <code>n // i</code> ne vračamo sproti,
+temveč jih le shranjujemo in jih vračamo kasneje.</p>
+
+<pre>from math import sqrt
+
+def delitelji(n):
+ for i in range(1, int(sqrt(n) + 1)):
+ if n % i == 0:
+ yield i
+ if i ** 2 != n:
+ ostali.append(n // i)
+ for e in ostali:
+ yield e</pre>
+
+<p>Nismo še čisto zmagali. Zdaj imamo 1, 2, 3, 6, 41, 21, 14, 7 - prva polovica
+je iz prvega <code>yield</code>a, druga (od 41 naprej) iz drugega. Te, druge,
+vrača v enakem vrstnem redu, v katerem jih je vstavljal v seznam, saj
+<code>append</code> pač vstavlja na konec.</p>
+
+<p>Pa vstavljajmo raje na začetek! Uporabimo <code>insert</code>.</p>
+
+<pre>from math import sqrt
+
+def delitelji(n):
+ for i in range(1, int(sqrt(n) + 1)):
+ if n % i == 0:
+ yield i
+ if i ** 2 != n:
+ ostali.insert(0, n // i)
+ for e in ostali:
+ yield e</pre>
+
+<p>Žal se je <code>insert</code>u modro izogibati. Za to, da vstavi element
+na prvo mesto, mora enega za drugim premakniti vse ostale. V teoriji (ki se jo
+boste učili drugo leto) je ta program enako počasen kot bi bil, če bi prvo
+zanko spustili prek <code>range(1, n + 1)</code>. S tem, ko smo zamenjali
+<code>append</code> z <code>insert</code>, smo, vsaj v teoriji, zapravili ves
+prihranek.</p>
+
+<p>To je preprosto urediti. Vstavljali bomo na konec, z <code>append</code>.
+Pač pa bomo seznam nato prehodili v obratnem vrstnem redu. To se da narediti
+z indeksiranjem (<code>for i in range(-1, -n - n, -1): yield ostali[i]</code>,
+vendar si bomo raje pomagali s priročno funkcijo <code>reversed</code>, ki
+obrne seznam.</p>
+
+<pre>def delitelji(n):
+ ostali = []
+ for i in range(1, int(sqrt(n)) + 1):
+ if n % i == 0:
+ yield i
+ if i ** 2 != n:
+ ostali.append(n // i)
+ for e in reversed(ostali):
+ yield e</pre>
+
+</html>