From 6dbe02d844b81d455dda3086fc0f212f7abdca06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Mo=C5=BEina?= Date: Mon, 17 Oct 2016 12:31:13 +0200 Subject: Added an exercise on recursion. Added lecture notes for lists and for loop. --- python/problems/lists_and_for/list_for_sl.html | 1276 ++++++++++++++++++++++++ python/problems/lists_and_for/sl.py | 5 +- python/problems/recursion/find_sum/common.py | 75 ++ python/problems/recursion/find_sum/en.py | 13 + python/problems/recursion/find_sum/find_sum.py | 19 + python/problems/recursion/find_sum/sl.py | 28 + 6 files changed, 1414 insertions(+), 2 deletions(-) create mode 100644 python/problems/lists_and_for/list_for_sl.html create mode 100644 python/problems/recursion/find_sum/common.py create mode 100644 python/problems/recursion/find_sum/en.py create mode 100644 python/problems/recursion/find_sum/find_sum.py create mode 100644 python/problems/recursion/find_sum/sl.py 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 @@ + + + + + + + + + + +

Seznami

+ +

Recimo, da smo zbrali teže šestih študentov. Seznam tež zapišemo takole:

+ +
teze = [74, 82, 58, 66, 61, 84]
+ +

Seznam (angl. list) + 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 [ in ]. Za primer sestavimo še + seznam imen študentov:

+ +
imena = ["Anze", "Benjamin", "Cilka", "Dani", "Eva", "Franc"]
+ +

in seznam, ki bo povedal, ali gre za študenta ali študentko

+ +
studentka = [False, False, True, False, True, False]
+ +

Seznami lahko vsebujejo tudi še hujšo eksotiko. Imamo lahko, recimo, seznam + seznamov - vanj bomo, prikladno, stlačili (pod)sezname teža-ime-spol:

+ +
podatki = [
+    [74, "Anze", False],
+    [82, "Benjamin", False],
+    [58, "Cilka", True],
+    [66, "Dani", False],
+    [61, "Eva", True],
+    [84, "Franc", False],
+    ]
+ +

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

+ +
teze = [74, 82, 58,
+        66, 61, 84]
+ +

Seznami lahko vsebujejo tudi še hujšo hujšo eksotiko. V resnici lahko + vsebujejo karkoli, celo, recimo, funkcije:

+ +
from math import *
+
+par_funkcij = [sin, cos, radians]
+ +

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

+ +

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

+ +

Seznam je lahko tudi prazen

+ +
prazen = []
+ +

ali pa ima en sam element

+ +
samo_edini = [42]
+ +

Ali pa je poln praznih seznamov

+ +
polnoprazen = [[], [], [], [], [], []]
+ + + +

Terka

+ +

Terka (angl. tuple) 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:

+ +
teze = (74, 82, 58, 66, 61, 84)
+imena = ("Anze", "Benjamin", "Cilka", "Dani", "Eva", "Franc")
+ +

Pri terkah se nam - zaradi tega, kje in kako jih uporabljamo, pogosteje + zgodi, da vsebuje elemente različnih tipov. Recimo takole:

+ +
student = (74, "Anze", False)
+ +

Tudi seznam teža-ime-spol bi raje naredil takole:

+ +
podatki = [
+    (74, "Anze", False),
+    (82, "Benjamin", False),
+    (58, "Cilka", True),
+    (66, "Dani", False),
+    (61, "Eva", True),
+    (84, "Franc", False),
+    ]
+ +

Tako kot seznami so tudi terke lahko prazne (()) ali pa imajo + po en sam element. Tu imamo težavo pri terki z enim samim elementom: Če bi + napisali (42) to ni seznam, temveč le 42 v oklepaju (odvečnem, + seveda). Da bi povedali, da gre za terko, moramo dodati še vejico:

+ +
samo_en = (42, )
+ +

Terko smemo, zanimivo, pisati tudi brez oklepajev.

+ +
>>> t = 1, 2, 3, 4
+>>> t
+(1, 2, 3, 4)
+ +

Ker to ni preveč pregledno, pa to počnemo le v posebnih primerih, na katere +bomo sproti opozorili.

+ + +

Razpakiranje v elemente

+ +

Včasih imamo seznam ali terko z nekaj elementi, ki bi jih radi priredili + več spremenljivkam. Recimo, da imamo

+ +
student = (74, "Anze", False)
+ +

Če želimo podatke prirediti spremenljivkam teza, + ime in je_zenska, bi to lahko storili takole:

+ +
>>> teza, ime, je_zenska = student
+ +

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.

+ +
>>> a, b, c = [1, 2, 3]
+>>> a
+1
+>>> a, b, c = "xyz"
+>>> a
+'x'
+>>> b
+'y'
+>>> c
+'z'
+ +

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 splitext, ki ji damo ime datoteke, +pa vrne njeno osnovno ime in njeno končnico.

+ +
>>> 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'
+ +

V resnici funkcija splitext vrne terko,

+ +
>>> from os.path import *
+>>> splitext(film)
+('Babylon 5 - 3x04 - Passing through Gethsemane', '.avi')
+ +

ki pa smo jo kar mimogrede razpakirali v zac in + konc.

+ +

Seveda bi lahko rezultat funkcije priredili tudi eni sami spremenljivki - + v tem primeru bi bila ta spremenljivka terka.

+ +
>>> t = splitext(film)
+>>> t
+('Babylon 5 - 3x04 - Passing through Gethsemane', '.avi')
+ +

In seveda bi lahko terko nato še razpakirali.

+ +
ime, koncnica = t
+ +

Kako v Pythonu zamenjamo vrednosti dveh spremenljivk?

+ +
a = "Alenka"
+b = "Tina"
+ +

Želeli bi, da bi a postal "Tina" in b "Alenka". + Naivnež bi napisal tale nesmisel:

+ +
a = b
+b = a
+ +

To seveda ne deluje. V prvi vrstici a-ju priredimo + b, se pravi, "Tina", v drugi vrstici pa b-ju + a, ki pa je medtem postal "Tina". Tako sta po tem + a in b enaka "Tina".

+ +

V drugih jezikih se navadno rešimo tako, da vrednost a-ja + spravimo na varno, preden ga povozimo.

+ +
tmp = a
+a = b
+b = tmp
+ +

V Pythonu pa to ni potrebno. Spremenljivki preprosto zapakiramo v terko, ki + jo takoj razpakiramo nazaj, a v obratnem vrstnem redu.

+ +
a, b = (b, a)
+ +

V resnici navadno ne pišemo tako, temveč izpustimo oklepaje.

+ +
a, b = b, a
+ +

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 + a-ju priredimo b in b-ju + a.

+ + +

Zanka for

+ +

Python ima dve vrsti zank: zanki while, ki jo že poznamo, dela + družbo for.

+ +

Zastavimo si preprosto nalogo (in ne čisto smiselno) nalogo: izpišimo teže + in kvadrate tež vseh študentov v seznamu. Se pravi (po slovensko):

+ +
za vsako težo s seznama teze stori tole:
+    izpiši težo in težo na kvadrat
+ +

V Pythonu pa prav tako, samo brez sklonov:

+
for teza in teze:
+    print(teza, teza ** 2)
+ +

V zanki for se skriva prirejanje. Zanka najprej + priredi spremenljivki teza prvi element seznama + teze. Nato se izvrši vsa koda bloka znotraj bloka + for - tako, kot se je dogajalo v zanki while. + V naslednji rundi priredi spremenljivki teza drugi element in + spet izvede kodo znotraj bloka, ... ter tako naprej do konca seznama.

+ +

Zdaj pa izpišimo vse teže, ki so večje od 70.

+ +
for teza in teze:
+    if teza > 70:
+        print(teza)
+ +

Napišimo program, ki pove, kako težak je najlažji študent.

+ +
najlazji = 1000
+for teza in teze:
+    if teza < najlazji:
+        najlazji = teza
+print(najlazji)
+ +

Je potrebno prevesti v slovenščino? Pa dajmo.

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

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 + najlazji = "ŽŽŽŽŽŽŽŽŽŽŽŽ" namesto najlazji = 1000. + (Tole se da sprogramirati tudi tako, da deluje za nize in za števila. A ne + bomo ubijali začetnikov s prezapletenimi programi.)

+ +

Mimogrede, Python ima že vdelano funkcijo min, ki vrne + najmanjši element seznama.

+ +

Kako bi izračunali vsoto elementov seznama?

+ +
s = 0
+for teza in teze:
+    s += teza
+ +

Pa poprečno vrednost? Za to moramo poznati dolžino seznama. Pove nam jo +funkcija len (okrajšava za length, če ji kot argument +podamo nek seznam).

+ +
s = 0
+for teza in teze:
+    s += teza
+s /= len(teze)
+ +

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.

+ +
s = 0
+for teza in teze:
+    s += teza
+if len(teze) > 0:
+    s /= len(teze)
+ +

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 for, kot se bomo kmalu + naučili.

+ +
>>> ime = "Cilka"
+>>> for crka in ime:
+...     print(crka)
+'C'
+'i'
+'l'
+'k'
+'a'
+ + +

Najdi takšnega, ki...

+ +

Kako bi ugotovili, ali vsebuje seznam kako sodo število?

+ +
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")
+ +

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.

+ +

Ne naredite klasične napake. Tole je narobe:

+ +
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")
+ +

Ta program se bo ob vsakem lihem številu delal, da doslej ni bilo še +nobenega sodega - zaradi imamo_sodo = False 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 imamo_sodo = False. +Rezultat bo tako napačen.

+ +

Kako pa ugotovimo, ali ima seznam sama soda števila?

+ +
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")
+ +

Spet bomo naredili podobno napako kot prej, če bomo pisali:

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

Program že takoj, ko vidi 1, ugotovi, da niso vsa števila v seznamu soda, +in postavi sama_soda = False. Vendar melje seznam naprej in ob +vsakem koraku znova nastavlja sama_soda. Ko pride do 72, postavi +sama_soda na True. To pa je ravno zadnje število; +sama_soda ostane True... pa smo tam.

+ + + +

Prekinjanje zank

+ +

Ko smo prejšnji teden pisali zanko while, 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...)

+ +

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.

+ +
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")
+ +

Ukaz break pomeni, da želimo prekiniti zanko. Program skoči +"ven" iz zanke in nadaljuje z izvajanjem ukazov, ki sledijo zanki.

+ +

Mimogrede, na podoben način lahko prekinemo tudi while.

+ +

Tule je bil break 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.

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

Brez break tole ne deluje: namesto prvega izpiše zadnje liho +število. Rešimo se lahko z dodatnim pogojem:

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

S tem, ko smo dodali and sama_soda smo poskrbeli, da se bosta +sama_soda = False in liho = e izvedla le prvič. Ko +bomo postavili sama_soda na False, bomo dosegli, da +pogoj ne bo nikoli nikoli več resničen.

+ +

Namesto dodatnega pogoja lahko napišemo break.

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

S tem se program lahko pravzaprav še bolj poenostavi. Ko zanko prekinemo, +e ostane, kar je bil. Torej ne potrebujemo več spremenljivke +liho.

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

Else po zanki

+ +

Medtem, ko je ukaz break zelo običajna žival v vseh + programskih jezikih, ima Python še eno posebnost, povezano z zankami. V + večini programskih jezikov lahko else uporabimo kot "odgovor" + na if. V Pythonu pa lahko else sledi tudi zanki + for ali while. V pogojnem stavku (if) + se koda v else izvede, če pogoj ni bil resničen. Po zanki se + else izvede, če se zanka ni prekinila zaradi + break. Torej, else se izvede pri zankah, ki so + se iztekle "po naravni poti".

+ +

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

+ +

Zgornji odstavek se približno, a ne čisto povsem prilega zadnjemu kosu +programa. V resnici je napisan po spodnjem programu:

+ +
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")
+ +

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 else: +poravnan je s for ne z if! Ta else +se torej nanaša na for. Kar napišemo v else-u za +for, se zgodi, če se zanka ni prekinila z breakom.

+ +

Preden naredimo še kakšen primer, si oglejmo še nekaj drugega.

+ + +

Štetje z zanko for

+ +
+

Tisti, ki poznajo kak drug, C-ju podoben jezik, so najbrž pričakovali + nekoliko + drugačno zanko.

+ +

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.

+ +
for(zacetek; pogoj; korak) {
+    koda;
+}
+ +

je isto kot

+ +
zacetek;
+while(pogoj) {
+    koda;
+    korak;
+}
+ +

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

+
+ +

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.

+ +

Za oboje bo poskrbela kar zanka for, ob pomoči priročne funkcije + range.

+ +

Funkcija range je na prvi pogled povsem neimpresivna. Vrača + sezname zaporednih števil.

+ +

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.

+ +
>>> 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]
+ +

Funkcija range 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 range(7)? Sedem. Zato + ker vsebuje prvi element in ne zadnjega, in ker se začne z 0.

+ +

Funkcija range je torej narejena praktično, ker se težko zmotimo +glede tega, koliko elementov bo vrnila.

+ +

Lepo se tudi sešteva. range(10, 15) + range(15, 20) so ravno +vsa števila od 10 do 20. Če bi bil range(10, 15) enak +[10, 11, 12, 13, 14, 15], bi se v +range(10, 15) + range(15, 20) število 15 ponovilo dvakrat.

+ +

Funkcija ima lahko še en, tretji argument. Ta predstavlja korak.

+ +
>>> range(5, 15, 2)
+[5, 7, 9, 11, 13]
+ +

Korak je lahko tudi negativen.

+ +
>>> 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]
+ +

Funkcijo range uporabljamo skoraj izključno v zankah + for.

+ +

V prvi zanki while, ki smo jo napisali, smo izpisali števila +od 1 do 10, takole nekako:

+ +
i = 1
+while i < 11:
+    print(i)
+    i += 1
+ +

Z zanko for je preprostejši.

+ +
for i in range(1, 11):
+    print(i)
+ +

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

+ +

Praštevila

+ +

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 + n, potem n ni praštevilo.

+ +
n = int(input("Vpiši število"))
+je_prastevilo = True
+for i in range(2, n):
+    if n % i == 0:
+        je_prastevilo = False
+ +

V začetku predpostavimo, da je število praštevilo + (je_prastevilo = True), če med števili med 2 in n + (for i in range(2, n)) naletimo na njegov delitelj + (if n % i == 0), pa presodimo, da število pač ne more biti + praštevilo (je_prastevilo = False). Mimogrede naj opozorimo + na napako, na katero stalno opozarjamo:

+ +
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!
+ +

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: break in else po zanki. + +

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")
+ +

Razpakiranje v zanki forcode> in še malo telovadbe

+ +

Nekoč na začetku predavanja smo imeli seznam študentov in njihovih tež:

+ +
podatki = [
+    (74, "Anze", False),
+    (82, "Benjamin", False),
+    (58, "Cilka", True),
+    (66, "Dani", False),
+    (61, "Eva", True),
+    (84, "Franc", False),
+    ]
+ +

Recimo, da bi radi izpisali imean študentov in njihove teže.

+ +

Če gremo prek seznama z zanko for, bomo dobivali terke. Te +lahko, vemo, razpakiramo.

+ +
for student in podatki:
+    teza, ime, spol = student
+    print(ime, ": ", teza)
+ +

Gre pa še dosti elegantneje: razpakiranje lahko opravimo kar znotraj +glave zanke, brez (nepotrebne) terke t:

+ +
for teza, ime, je_zenska in podatki:
+    print(ime, ": ", teza)
+ +

Program torej pravi

+ +
za vsako trojko (teza, ime, je_zenska) iz seznama podatki:
+    izpisi ime in tezo
+ +

Lahko pa se tudi malo igramo in izrišemo graf:

+ +
for teza, ime, je_zenska in podatki:
+    print(ime + " " + "*"*teza)
+ +

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

+ +
for teza, ime, je_zenska in podatki:
+    print(" "*(15 - len(ime)) + ime + " " + "*"*teza)
+ + +

Zanka po dveh seznamih

+ +

Zdaj pa izpišimo imena in teže, pri tem, da se le-ta nahajajo v ločenih + seznamih.

+ +
teze = [74, 82, 58, 66, 61, 84]
+imena = ["Anze", "Benjamin", "Cilka", "Dani", "Eva", "Franc"]
+studentka = [False, False, True, False, True, False]
+ +

Naivno bi se lotili takole

+ +
+for ime in imena:
+    print(ime, ":")
+
+ +

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

+ +
+for ime in imena:
+    for teza in teze:
+        print(ime, ": ", teza)
+
+ +

Rezultat je nepričakovan za vse tiste, ki ga niso pričakovali. Program + namreč izpiše

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

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:

+ +
+za vsako ime na seznamu ime stori tole:
+    za vsako tezo na seznamu tez stori tole:
+        izpisi ime in tezo
+ +

Kdor ne razume, kaj se je zgodilo in zakaj, naj to nujno premisli, da se ne +bo kasneje učil na lastnih napakah.

+ +

Ko bo to opravljeno, bo razumel: potrebujemo samo eno zanko, ki gre + istočasno po obeh seznamih. Aha, takole?

+ +
+for ime in imena:
+for teza in teze:
+    print(ime, ": ", teza)
+
+ +

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 zip + združi dva seznama (ali več seznamov) v seznam terk.

+ +
>>> 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)]
+ +

Opomba: tudi tale izpis je iz starega Pythona. Od verzije 3.0 si + lahko le predstavljamo, da zip dela takole. V resnici naredi + nekaj majčkeno drugačnega (kaj, je za nas ta mesec še prezapleteno), + rezultat pa je isti.

+ +

Zipamo lahko vse, prek česar lahko naženemo zanko for. Torej ne le seznamov, + temveč tudi nize, terke in še kaj.

+ +
>>> zip("abcd", "ABCD")
+[('a', 'A'), ('b', 'B'), ('c', 'C'), ('d', 'D')]
+>>> zip("abcd", range(4))
+[('a', 0), ('b', 1), ('c', 2), ('d', 3)]
+ +

Rešimo torej nalogo: izpišimo imena in teže iz ločenih seznamov.

+ +
+for ime, teza in zip(imena, teze):
+    print(ime, ": ", teza)
+
+ +

Za konec pa izpišimo še poprečno težo študentk.

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

Dolžina seznama, terke, niza

+ +

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 1, 2, 3, 4 je + dolžine 4 in niz "Benjamin" je dolžine 8. Dolžine reči, ki imajo dolžino, + nam pove funkcija len. To sem napisal tako imenitno, da bom + moral še enkrat, da bo sploh kdo razumel: funkcija lensprejme + en argument, recimo seznam, terko ali niz in kot rezultat vrne dolžino + tega seznama, terke ali niza.

+ +
>>> b = 'Benjamin'
+>>> len(b)
+8
+>>> len(podatki)
+6
+>>> len((1, 2, 3))
+3
+>>> len(12)
+Traceback (most recent call last):
+  File "", line 1, in 
+TypeError: object of type 'int' has no len()

+ +

Morda je koga presenetilo zadnje: številka 12 bi lahko bila dolga 12, ne? + Ali pa 2, ker ima dve števki? Ne. Funkcija len v bistvu pove + število elementov, ki jih vsebuje podani argument. Niz "Benjamin" + vsebuje 8 znakov, seznam podatki vsebuje 6 podseznamov in + terka (1, 2, 3) ima tri elemente. Število 12 pa nima + elementov.

+ +

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

if s:
+    print("Seznam s ni prazen")
+else:
+    print("Seznam s je prazen")
+ + +

Indeksiranje

+ +

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.

+ +
>>> imena
+['Anze', 'Benjamin', 'Cilka', 'Dani', 'Eva', 'Franc']
+ +

Spremenljivka imena 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:

+ +
>>> imena[2]
+'Cilka'
+ +

Se pravi, imena[2] vrne drugi element seznama.

+ +

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

+ +

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

+ +
>>> [3, 1, 4, 1, 5, 9][2]
+4
+ +

Prvi oklepaji definirajo seznam, drugi zaprejo indeks, 2, ki pove, kateri + element tega seznama nas zanima.
+[
+Zveni bedasto in neuporabno? Potem mi povejte, ali je bedasto in neuporabno + tole: +

["moski", "zenska"][spol[0]]
+in tole +
"MZ"[spol[0]]
+] +

+ +

Na enak način kot sezname indeksiramo tudi terke in nize.

+
>>> 'Benjamin'[0]
+'B'
+>>> 'Benjamin'[2]
+'n'
+ +

Kadar seznam vsebuje sezname, bomo včasih uporabljali dvojne indekse. + Spomnimo se seznama podatki: vsebuje šest elementov, in ti + elementi so spet seznami s po tremi elementi, ki predstavljajo težo, ime + in spol. Vzemimo, recimo, drugi element:

+ +
>>> en_student = podatki[2]
+>>> en_student
+[58, "Cilka", True]
+ +

Zdaj lahko o Cilki nekaj povemo.

+ +
>>> print(en_student[1], "tehta", en_student[0], "kilogramov.")
+Cilka tehta 58 kilogramov.
+ +

Lahko pa opravimo oboje v enem zamahu.

+ +
>>> print(podatki[2][1], "tehta", podatki[2][0], "kilogramov.")
+ +

podatki[2][0] pomeni (če preberemo z desne) ničti element + drugega elementa seznama podatki. Če se vam zdi to hudo, vas + lahko potolažim, da ni: v resnici je še bolj grozno. Ker je + podatki[2][1] niz, se lahko vprašamo po, recimo, tretji črki + tega niza. + +

>>> print("Tretja crka niza", podatki[2][1], "je", podatki[2][1][3])
+ +

(V kost za glodanje C-jašem pa naj vprašam, ali + podatki[2][1][3][0][0][0][0] kaj izpiše ali se pritoži, da je + tu nekaj narobe. Ampak to je pa res neuporabno.)

+ +

Kaj se zgodi, če je indeks prevelik? Nič lepega.

+ +
>>> "Benjamin"[100]
+Traceback (most recent call last):
+  File "", line 1, in 
+IndexError: string index out of range
+ +

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 len, minus 1.

+ +

Python (in še marsikateri današnji jezik) ima še en trik: indeksiranje s + konca: -1 je zadnji element, -2 predzadnji in tako naprej.

+ +
>>> 'Benjamin'[-1]
+'n'
+>>> 'Benjamin'[-2]
+'i'
+>>> 'Benjamin'[-3]
+'m'
+ + +

Rezanje

+ +

Poleg indeksiranja, ki vrača elemente nizov, seznamov, terk (in še česa), + pozna Python še rezanje (slice), 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!).

+ +

Smo to že kje videli? Smo, seveda. Funckija range uporablja +natančno isto logiko. Tako kot pri range, je tudi pri rezanju +to odlična ideja.

+ +

Oglejmo si, kaj na to pravi Benjamin.

+
>>> b[2:5]
+'nja'
+>>> b[5:8]
+'min'
+>>> b[2:5]+b[5:8]
+'njamin'
+ +

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.

+ +
>>> b[:5]
+>>> b[:5]
+'Benja'
+>>> b[5:]
+'min'
+ +

b[:5] vrne prvih pet elementov. b[5:] vrne vse + od petega naprej; ker štejemo od 0, to pomeni, da izpustimo prvih pet. Se + pravi, če želimo nizu s odbiti prvih pet znakov, bomo rekli

+ +
s = s[5:]
+ +

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:

+ +
>>> b[:5]+"X"+b[5:]
+'BenjaXmin'
+ +

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.

+ +

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

+ +
>>> b[-5:-2]
+'jam'
+ +

No, tegale najbrž ne uporabimo velikokrat. Pač pa nas pogosto zanimajo, + recimo, zadnji štirje znaki. Ali pa vsi razen zadnjih štirih.

+ +
>>> film = "Babylon 5 - 3x04 - Passing through Gethsemane.avi"
+>>> film[-4:]
+'.avi'
+>>> film[:-4]
+'Babylon 5 - 3x04 - Passing through Gethsemane'
+ +

Mimogrede, tole sicer varneje delamo s funkcijo, ki smo jo že omenili, + namreč splitext, ki prejme kot argument ime datoteke in vrne + terko z dvema elementoma, imenom datoteke brez končnice in končnico.

+ +

Kako pa bi od niza odbili prve tri in zadnja dva znaka? Takole:

+ +
>>> b[3:-2]
+'jam'
+ +

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

+ + + + + + + + + +
012345678 +
Benjamin
-8-7-6-5-4-3-2-1 +
+ +

Vsi znaki med drugim in petim so 'nja', znaki od 3 do -2 so 'jam'... In tako + naprej.

+ +

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.

+ +

Poleg meja rezine lahko podamo tudi korak. Namesto vsakega znaka + lahko zahtevamo, recimo, vsak drugi znak, tako da dodamo še eno dvopičje, + ki mu sledi velikost koraka.

+ +
>>> '0123456789'[2:9]
+'2345678'
+>>> '0123456789'[2:9:2]
+'2468'
+>>> '0123456789'[2:9:3]
+'258'
+>>> '0123456789'[2:9:4]
+'26'
+ +

Korak je lahko, tako kot pri range tudi negativen! V tem + primeru je potrebno zamenjati meji - prva mora biti višja od druge.

+ +
>>> '0123456789'[9:2:-1]
+'9876543'
+>>> '0123456789'[9:2:-2]
+'9753'
+ +

Meje smemo seveda spet tudi izpuščati:

+ +
>>> '0123456789'[9::-1]
+'9876543210'
+ +

Takole obrnemo predavatelja.

+ +
>>> 'demšar janez'[::-1]
+'zenaj rašmed'
+ +

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 + for, recimo, ki jih bomo vsak čas spoznali.

+ +

Poglejmo si torej še rezanje seznamov.

+ +
>>> 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']
+ +

Že videno. Rezanje + seznamov se vede enako kot rezanje nizov. Rezanje terk pa prav tako.

+ + +

Spreminjanje seznamov z indeksiranjem in rezanjem

+ +

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.

+ +
>>> imena
+['Anze', 'Benjamin', 'Cilka', 'Dani', 'Eva', 'Franc']
+>>> imena[3] = "Daniel"
+>>> imena
+['Anze', 'Benjamin', 'Cilka', 'Daniel', 'Eva', 'Franc']
+ +

Tako kot prej lahko tudi zdaj indeksiramo od spredaj ali od zadaj.

+ +

(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 array, je bližji pythonov slovar + (dictionary, dict), ki ga bomo spoznali v + prihodnosti.)

+ +

Pa rezine? Glede na to, da je rezina podseznam, moramo tudi pri prirejanju + rezin prirejati podsezname.

+ +
>>> imena
+['Anze', 'Benjamin', 'Cilka', 'Daniel', 'Eva', 'Franc']
+>>> imena[1:4]=["Ben", "Cecilija", "Dani"]
+>>> imena
+['Anze', 'Ben', 'Cecilija', 'Dani', 'Eva', 'Franc']
+ +

Mora biti seznam, ki ga prirejamo, enako dolg kot rezina, ki jo bomo + povozili? Ne, čemu? Takole zamenjamo tri z dvema:

+ +
>>> imena[1:4]=["Ben-Cecil", "Daniel"]
+>>> imena
+['Anze', 'Ben-Cecil', 'Daniel', 'Eva', 'Franc']
+
+ +

Ali pa nobenega s tremi:

+ +
>>> imena[2:2] = ["D2", "D3", "D4"]
+>>> imena
+['Anze', 'Ben-Cecil', 'D2', 'D3', 'D4', 'Daniel', 'Eva', 'Franc']
+ +

Z rezinami lahko tudi pobrišemo del seznama.

+ +
>>> 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']
+ +

Za brisanje obstaja še veliko drugih načinov, recimo tale

+
>>> imena
+['Anze', 'Ben-Cecil', 'Daniel', 'Eva', 'Franc']
+>>> del imena[2]
+>>> imena
+['Anze', 'Ben-Cecil', 'Eva', 'Franc']
+>>> del imena[1:3]
+>>> imena
+['Anze', 'Franc']
+ +

Č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!

+ +
>>> b = 'Benjamin'
+>>> b[2]='a'
+Traceback (most recent call last):
+  File "", line 1, in 
+TypeError: 'str' object does not support item assignment
+ +

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'?

+ +
>>> b = b[:2]+'a'+b[3:]
+>>> b
+'Beajamin'
+ +

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.

+ + +

Računske operacije na seznamih (in drugod)

+ +

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, True in False. 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.

+ +

Lahko sezname seštevamo? Kaj dobimo, če seštejemo [2, 5, -1] in + [3, 7, 4]? Dobimo [5, 12, 3] ali + [2, 5, -1, 3, 7, 4]? 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 'abc' + 'def' enako 'abcdef', + naj bo tudi [2, 5, -1] + [3, 7, 4] enako + [2, 5, -1, 3, 7, 4].

+ +

Podobno je z množenjem. [2, 5, -1] * 2 bi moralo biti po vsej + logiki enako [2, 5, -1] + [2, 5, -1], se pravi + [2, 5, -1, 2, 5, -1]. In tudi je.

+ +

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.

+ +

Poleg + in * pa poznajo vsi trije še dva + operatorja, in in not in. S prvim vprašamo, ali + seznam oz. terka vsebujeta določen element in ali niz vsebuje določen + podniz.

+ +
>>> 1 in [1, 2, 3]
+True
+>>> 4 in [1, 2, 3]
+False
+>>> 'in' in 'Benjamin'
+True
+>>> 'an' in 'Benjamin'
+ +

Drugi je seveda ravno nasproten, z njim se vprašamo ali seznam (niz) + ne vsebuje elementa (podniza).

+ +
>>> 1 not in [1, 2, 3]
+False
+>>> 4 not in [1, 2, 3]
+True
+>>> 'in' not in 'Benjamin'
+False
+>>> 'an' not in 'Benjamin'
+True
+ +

V resnici not in ni prav potreben, saj je + x not in l isto kot not x in l.

+ + + + + 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 = '' + \ + 'Seznami in terke, operacije na seznami, zanka for.' \ 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 = '''\ +

(translation missing)

''' + +hint = { + 'plan': '''\ +

(translation missing)

''', + + 'no_input_call': '''\ +

(translation missing)

''', +} 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 = '''\ +

+Napiši funkcijo find_sum(xs, gs), ki kot argument prejme seznam +pozitivnih števil in število, ki predstavlja ciljno vsoto. Funkcija vrne +True, kadar lahko sestavimo ciljno vsoto iz števil v seznamu.

+ +
+>>> find_sum([2,7,3,1,4], 10)
+True
+>>> find_sum([2,3,2,4], 10)
+False
+
+''' + +plan = [] + +hint = { + 'final_hint': ['''\ +

Program je pravilen!
+

+'''] +} -- cgit v1.2.1