Naloga

Veliko latinskih napisov, ki obeležujejo kak pomemben dogodek, je napisanih v obliki kronograma: če seštejemo vrednosti črk, ki predstavljajo tudi rimske številke (I=1, V=5, X=10, L=50, C=100, D=500, M=1000), dajo letnico dogodka.

Tako, recimo, napis na cerkvi sv. Jakoba v Opatiji, CVIVS IN HOC RENOVATA LOCO PIA FVLGET IMAGO SIS CVSTOS POPVLI SANCTE IACOBE TVI, da vsoto 1793, ko je bila cerkev prenovljena (o čemer govori napis).

Pri tem obravnavamo vsak znak posebej: v besedil EXCELSIS bi prebrali X+C+L+I = 10+100+50+1 = 161 in ne XC + L + I = 90 + 50 + 1 = 141.

Napiši program, ki izračuna letnico za podani niz.

Naloga je lahka, vendar se bo izkazala še za poučno, ko jo bomo znali rešiti učinkoviteje kot zdaj.

Rešitev

Takole znamo

Prva rešitev je tisto, kar bi morali znati res vsi. Kdor ni, naj resno pljune v roke, dokler še ni prepozno.

def kronogram(s): v = 0 for c in s: if c=="I": v += 1 elif c=="V": v += 5 elif c=="X": v += 10 elif c=="L": v += 50 elif c=="C": v += 100 elif c=="D": v += 500 elif c=="M": v += 1000 return v

(Ob tem se je marsikdo, ki že zna kak drug jezik, vprašal, ali Python nima stavka switch. Nima ga, vendar ga v resnici niti ne pogrešamo, saj (skoraj) vedno obstaja lepša rešitev, za katero ga ne potrebujemo.)

Lepša rešitev, ki še ne presega tega, kar smo se učili pri tem predmetu, dela takole: vzame seznam parov (črka, vrednost); za vsako črko prešteje, kolikokrat se pojavi v nizu in k vsoti prišteje tolikokrat vrednost znaka. Če se, recimo, pojavijo trije znaki L, prišteje 3*50.

vredt = [("I", 1), ("V", 5), ("X", 10), ("L", 50), ("C", 100), ("D", 500), ("M", 1000)] def kronogram(s): v = 0 for z, f in vredt: v += f*s.count(z) return v

Takole bomo znali ... vsak čas

Ko se bomo (kmalu, kmalu, ne bodite nestrpni) naučili delati s slovarji, bomo lahko nalogo rešili takole

stevke = {"I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000} def kronogram(s): v = 0 for c in s: if c in stevke: v += stevke[c] return v ali pa takole: def kronogram(s): v = 0 for c in s: v += stevke.get(c, 0) return v

Takole bomo znali decembra

Tule sta le gornji različici povedani hitrejše. Varianta s terkami: vredt = [("I", 1), ("V", 5), ("X", 10), ("L", 50), ("C", 100), ("D", 500), ("M", 1000)] def kronogram(s): return sum(f*s.count(v) for v, f in vredt) in s slovarji stevke = {"I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000} def kronogram(s): return sum(stevke.get(c, 0) for c in s)

Zabavnejše rešitve

Tole pravzaprav še ne presega našega znanja: v nizu zamenjamo vse znake V s petimi Iji, vse znake X z desetimi Iji... in tako naprej in na koncu vse znake M s 1000 Iji, ter preštejemo, koliko Ijev imamo.

def kronogram(s): print(s.replace("V", "I"*5).replace("X", "I"*10) .replace("L", "I"*50).replace("C", "I"*100) .replace("D", "I"*500).replace("M", "I"*1000).count("I")) Tudi v tem programu ni popolnoma nič takšnega, česar še ne znamo, samo malo bolj hecno je napisan: v tej funkciji nikjer ne piše, da je I vreden 1, V 5, X 10... pa vendar deluje. Kako to? def kronogram(s): v = 0 for c in s: i = "IVXLCDM".find(c) v += i>=0 and (1+4*(i%2))*10**(i//2) return v Regularni izrazi v kombinaciji s slovarjem... mislil sem, da bodo bolj zabavni, pa niti niso. import re stevke = {"I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000} def kronogram8(s): return len(re.sub(".", lambda x:"."*stevke.get(x.group(), 0), s))

Zmagovalna rešitev

Od vseh rešitev, ki sem jih napisal, mi je najbolj všeč tale. Kratka je in jedrnata, ne uporablja informacije o tem, da je V vredna 5 in tako naprej, poleg tega pa ima zelo lep argument v funkciji map. Lepotica, res. def kronogram(s): return sum(i>=0 and (1+4*(i%2))*10**(i//2) for i in map("IVXLCDM".find, s))
Last modified: Monday, 29 October 2012, 10:11 PM