1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
|
<!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>Slovar</h2>
<p>Poglejmo še enkrat sezname. Seznam je nekakšna zbirka nekih poljubnih
reči, pri čemer je vsaka reč v svojem "predalčku", predalčki pa so
oštevilčeni. Prvi ima številko 0, drugi 1, tretji 2 in tako naprej. Za
vsak predalček lahko pogledamo, kaj je v njem, spremenimo njegovo vsebino,
predalčke lahko odvzemamo in dodajamo.
<pre>>>> s = [6, 9, 4, 1]
>>> s[1]
9
>>> s.append(7)
>>> s
[6, 9, 4, 1, 7]
>>> del s[3]
>>> s
[6, 9, 4, 7]
>>> s[-2:]
[4, 7]</pre>
<p>Slovar (angl. <em>dictionary</em>, podatkovni tip pa se imenuje
<code>dict</code>) je podoben seznamu, le da se na predalčke sklicujemo z
njihovimi <em>ključi</em> (angl. <em>key</em>, ki so lahko števila, lahko
pa tudi nizi, logične vrednosti, terke... Temu, kar je shranjeno v predalčku
bomo rekli <em>vrednost</em> (<em>value</em>). </p>
<p>Seznam in slovar se že od daleč (če niste kratkovidni) razlikujeta po
oglatosti. Seznam smo opisali tako, da smo v <em>oglatih oklepajih</em>
našteli njihove <em>elemente</em>. Slovar opišemo tako, da v <em>zavitih
oklepajih</em> naštejemo pare <em>ključ: vrednost</em>.
<p>Stalno omenjani Benjamin si lahko takole sestavi slovar s telefonskimi
številkami svojih oboževalk:
<pre>stevilke = {"Ana": "041 310239", "Berta": "040 318319", "Cilka": "041 103194", "Dani": "040 193831",
"Eva": "051 123123", "Fanči": "040 135367", "Helga": "+49 175 4728 475"}</pre>
<p>Do vrednosti, shranjenih v slovarju, pride podobno kot do vrednosti v
seznamu: z indeksiranjem, le da v oglate oklepaje ne napiše zaporedne
številke, temveč ključ.</p>
<pre>>>> stevilke["Ana"]
'041 310239'
>>> stevilke["Dani"]
'040 193831'</pre>
<p>Slovarji ne poznajo vrstnega reda. V slovarju ni prvega, drugega, tretjega
elementa. Ne le zato, ker oznake niso številke. Ne, slovar si v resnici ne
zapomni vrstnega reda, v katerem smo našteli elemente, niti ne vrstnega
reda, v katerem jih dodajamo.</p>
<pre>>>> stevilke
{'Dani': '040 193831', 'Fanči': '040 135367', 'Helga': '+49 175 4728 475',
'Eva': '051 123123', 'Cilka': '041 103194', 'Berta': '040 318319',
'Ana': '041 310239'}</pre>
<p>(Čemu?! Čemu je to potrebno? Čemu zmeša vrstni red?! Mar ne more zlagati
teh reči lepo po vrsti? Izvedeli boste drugo leto. V resnici ni zmešan,
temveč prav premeteno premetan. V tem, drugače preurejenem seznamu lahko išče
veliko hitreje.)</p>
<p>Ker ni vrstnega reda, tudi ni rezin.</p>
<pre>>>> stevilke["Ana":"Cilka"]
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
TypeError: unhashable type</pre>
<p>Tole sporočilo o napaki je sicer zelo čudno, vendar povsem smiselno. Boste
razumeli, ko boste veliki. ;)</p>
<p>O "mehaniki" slovarjev morate vedeti le tole: slovarji so hitri, saj
elementov v resnici ne iščejo. Na nek za zdaj skrivnosten način "vedo", kje
je vrednost, ki pripada danemu ključu. Ne da bi iskali, vedo, kam morajo
pogledati. Tudi dodajanje novih elementov v slovar je zelo hitro, enako tudi
brisanje. Cena, ki jo plačamo za to, je dvojna. Ključi so lahko le
podatkovni tipi, ki so nespremenljivi. Nespremenljivi podatkovni tipi so,
vemo, nizi, števila, logične vrednosti in terke. Zgoraj smo kot ključ
uporabili niz. To je OK. Če bi poskušali kot ključ uporabiti seznam, ne bi
šlo. (V resnici je stvar malenkost bolj zapletena, ključ je lahko vsak
podatkovni tip, ki je "razpršljiv", vendar to ni za bruce). Omejitve
veljajo le za ključe. Vrednost je lahko karkoli.</p>
<p>Drugi obrok cene, ki jo plačamo za učinkovitost slovarjev, je poraba
pomnilnika: slovar porabi nekako dvakrat več pomnilnika kot seznam. Koliko,
točno, več, je odvisno od velikost slovarja in tega, kaj shranjujemo.
Pri tem predmetu se s pomnilnikom ne obremenjujte.</p>
<p>Aha, še tretji obrok cene, iz drobnega tiska: vsak ključ se lahko pojavi
največ enkrat. Če poskušamo prirediti neko vrednost ključu, ki že obstaja, le
povozimo staro vrednost. Ampak to je tako ali tako pričakovati. Tudi v
seznamu nimamo nikoli dveh različnih elementov na istem mestu.</p>
<h4>Kaj lahko počnemo s slovarji?</h4>
<p>Lahko jim dodajamo nove elemente: preprosto priredimo jim nov element.</p>
<pre>>>> stevilke["Iva"] = "040 222333"
>>> stevilke
{'Dani': '040 193831', 'Fanči': '040 135367', 'Iva': '040 222333',
'Helga': '+49 175 4728 475', 'Eva': '051 123123', 'Cilka': '041 103194',
'Berta': '040 318319', 'Ana': '041 310239'}</pre>
<p><code>append</code> ne obstaja, saj nima smisla: ker ni vrstnega reda, ne
moremo dodajati na konec. Prav tako (ali pa še bolj) ne obstaja
<code>insert</code>, saj prav tako (ali pa še bolj) nima smisla.
<p>Vprašamo se lahko, ali v seznamu obstaja določen ključ.</p>
<pre>>>> "Cilka" in stevilke
True
>>> "Jana" in stevilke
False</pre>
<p>Če se Cilka skrega z Benjaminom, jo lahko le-ta pobriše (mislim, pobriše
njeno številko).</p>
<pre>>>> del stevilke["Cilka"]
>>> stevilke
{'Dani': '040 193831', 'Fanči': '040 135367', 'Iva': '040 222333',
'Helga': '+49 175 4728 475', 'Eva': '051 123123',
'Berta': '040 318319', 'Ana': '041 310239'}
>>> "Cilka" in stevilke
False</pre>
<p>Če gremo prek slovarjev z zanko <code>for</code>, dobimo vrednosti
ključev.</p>
<pre>>>> for ime in stevilke:
... print(ime)
...
Dani
Fanči
Iva
Helga
Eva
Berta
Ana</pre></p>
Seveda lahko ob vsakem imenu izpišemo tudi številko.
<pre>>>> for ime in stevilke:
... print(ime + ":", stevilke[ime])
...
Dani: 040 193831
Fanči: 040 135367
Iva: 040 222333
Helga: +49 175 4728 475
Eva: 051 123123
Cilka: 041 103194
Berta: 040 318319
Ana: 041 310239</pre>
<p>Vendar to ni najbolj praktično. Pomagamo si lahko s tremi metodami slovarja,
ki vrnejo vse ključe, vse vrednosti in vse pare ključ-vrednost. Imenujejo
se (ne preveč presenetljivo) <code>keys()</code>, <code>values()</code> in
<code>items()</code>. Delamo se lahko, da vračajo sezname ključev,
vrednosti oziroma parov ... čeprav v resnici vračajo le nekaj, prek česar
lahko gremo z zanko <code>for</code>.
<p>Najprej poglejmo <code>items()</code>.</p>
<pre>>>> for t in stevilke.items():
... ime, stevilka = t
... print(ime + ":", stevilka)</pre>
<p>Tole zgoraj je bilo seveda grdo. Lepo se reče takole:</p>
<pre>>>> for ime, stevilka in stevilke.items():
... print(ime + ":", stevilke)</pre>
<p>Tako kot sem ob zanki for težil, da ne uporabljajte
<code>for i in range(len(s))</code> temveč <code>for e in s</code> in da
že v glavi zanke razpakirajte terko, kadar je to potrebno, bom težil tudi
tule: uporabljajte <code>items</code> in vaši programi bodo preglednejši in
s tem pravilnejši.</p>
<p>Metoda <code>values</code> vrne vse vrednosti, ki so v slovarju. V našem
primeru torej telefonske številke. Metodo <code>values</code> človek včasih
potrebuje, prav pogosto pa ne.</p>
<p>V nasprotju s tem metodo <code>keys</code> potrebujejo samo študenti. Ne vem
točno, zakaj sploh obstaja. No, vem, zato da študenti lahko pišejo
<code>for ime in stevilke.keys()</code> namesto
<code>for ime in stevilke</code>. Druge uporabne vrednosti pa ne vidim. :)</p>
<p>Skratka: ne uporabljajte metode <code>keys</code>.</p>
<p>Slovar ima še dve metodi, ki smo ju videli tudi pri seznamu: metodo, ki
sprazni slovar in drugo, ki naredi nov, enak slovar.</p>
<pre>>>> stevilke2 = stevilke.copy()
>>> stevilke2
{'Dani': '040 193831', 'Fanči': '040 135367', 'Iva': '040 222333',
'Helga': '+49 175 4728 475', 'Eva': '051 123123',
'Berta': '040 318319', 'Ana': '041 310239'}
>>> stevilke.clear()
>>> stevilke
{}
>>> stevilke2
{'Dani': '040 193831', 'Fanči': '040 135367', 'Iva': '040 222333',
'Helga': '+49 175 4728 475', 'Eva': '051 123123',
'Berta': '040 318319', 'Ana': '041 310239'}</pre>
<p>Omenimo le še eno od slovarjevih metod, <code>get</code>. Ta dela podobno
kot indeksiranje, <code>stevilke.get("Ana")</code> naredi isto kot
<code>stevilke["Ana"]</code>. Metodo <code>get</code> uporabimo, kadar ni
nujno, da je ključ v seznamu in želimo v tem primeru dobiti neko privzeto
vrednost. Privzeto vrednost podamo kot drugi argument.</p>
<pre>>>> stevilke.get("Ema", "ni številke")
'040 584928'
>>> stevilke.get("Greta", "ni številke")
'nimam stevilke'</pre>
<p>Še enkrat: ne pišite <code>stevilke.get("Ema")</code>. To je čudno.
Piše se <code>stevilke["Ema"]</code>. Metodo <code>get</code> uporabite le
takrat, kadar niste prepričani, da slovar vsebuje ta ključ.</p>
<h4>Primer: kronogrami</h4>
<p>Neka naloga je šla takole.</p>
<p>Veliko latinskih napisov, ki obeležujejo kak pomemben dogodek, je napisanih
v obliki <a href="http://en.wikipedia.org/wiki/Chronogram">kronograma</a>:
č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.</p>
<p>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).</p>
<p>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.</p>
<p>Napiši program, ki izračuna letnico za podani niz.</p>
<p>Očitna rešitev je:</p>
<pre>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</pre>
<p>Pri njej vsi, ki poznajo stavek <code>switch</code> oz. <code>case</code>
postokajo, kako je možno, da Python tega stavka nima. (Pustimo ob strani,
ali je tole res toliko daljše od <code>switch</code>, sploh če program nespodobno
nabijemo skupaj:</p>
<pre>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</pre>
<p>). Ne, nima ga, ker ga skoraj nikoli ne potrebujemo. Tisto, kar delamo z
njim, pogosto rešimo (tudi) s slovarji.</p>
<p>V slovar si bomo zapisali, katero številko pomeni katera črka.</p>
<pre>stevke = {"I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000}</pre>
<p>Funkcija zdaj deluje tako, da gre prek niza in za vsako črko preveri, ali je v
slovarju. Če je ni, ne pomeni številke; če je, prištejemo toliko, kolikor je
vredna.</p>
<pre>def kronogram(s):
v = 0
for c in s:
if c in stevke:
v += stevke[c]
return v</pre></p>
<p>Še hitreje gre z <code>get</code>; če črke ni, je njena privzeta vrednost 0.
</p>
<pre>def kronogram(s):
v = 0
for c in s:
v += stevke.get(c, 0)
return v</pre>
<p>Ob tem si ne moremo kaj, da ne bi poškilili za en mesec naprej, ko se bomo
učili funkcijskega programiranja in znali biti še krajši in jedrnatejši,
spet predvsem po zaslugi slovarjev.</p>
<pre>def kronogram(s):
return sum(stevke.get(c, 0) for c in s)</pre>
<h2>Slovarji s privzetimi vrednostmi</h2>
<p>Slovarji so uporabne reči. V veliko primerih pa uporabimo neko različico
slovarja, ki ima še eno dodatno sposobnost.</p>
<p>Denimo, da smo dobili v preskušanje igralno kocko, za katero nas zanima, ali
goljufa. Stokrat smo jo vrgli in zabeležili rezultate.</p>
<pre>meti = [4, 4, 4, 3, 2, 3, 5, 3, 3, 4, 6, 6, 6, 1, 3,
6, 6, 4, 1, 4, 6, 1, 4, 4, 4, 6, 4, 6, 5, 5, 6, 6, 2, 4, 4, 6,
3, 2, 6, 1, 3, 6, 3, 2, 6, 6, 4, 6, 4, 2, 4, 4, 1, 1, 6, 2, 6,
6, 4, 3, 4, 2, 6, 5, 6, 3, 2, 5, 1, 5, 3, 6, 4, 6, 2, 2, 4, 1,
4, 4, 3, 1, 4, 2, 1, 3, 1, 4, 6, 1, 1, 3, 4, 1, 4, 3, 2, 4, 6, 6]</pre>
<p>Zanima nas, kolikokrat je padla katera številka. Nič lažjega. Najprej si
pripravimo seznam s sedmimi ničlami.</p>
<pre>>>> kolikokrat = [0] * 7
>>> kolikokrat
[0, 0, 0, 0, 0, 0, 0]</pre>
<p>Zdaj nam bo, recimo <code>kolikokrat[3]</code> povedal, kolikokrat je padla
trojka. (Čemu sedem? Saj ima kocka samo šest ploskev. Boste videli. Finta.)
Zdaj pa štejmo: pojdimo čez vse mete in povečujmo števce.</p>
<pre>>>> for met in meti:
... kolikokrat[met] += 1
...
>>> kolikokrat
[0, 14, 12, 15, 27, 6, 26]</pre>
<p>(Kje je finta? <code>kolikokrat[0]</code> bo pove, kolikokrat je padla ničla.
Nikoli pač. Napišite isto reč s seznamom dolžine šest, ne sedem. Ni problem,
ampak tako, kot smo nardili je preprostejše.)</p>
<p>(Kocka je res videti nekoliko sumljiva: štirke in šestice so nekam pogoste
na račun petk. A pustimo statistikom vprašanje, ali je to še lahko
naključno ali ne.)</p>
<p>Ups, nalogo smo rešili kar s seznamom! Da, to gre, ker so "predalčki"
seznama oštevilčeni, tako kot ploskve kocke. Tole pa ne bo šlo: tule je
seznam telefonskih številk deklet, ki jih je danes klical Benjamin. Katero
je poklical kolikokrat?</p>
<pre>klici = ['041 103194', '040 193831', '040 318319', '040 193831', '041 310239',
'040 318319', '040 318319', '040 318319', '040 193831', '040 193831',
'040 193831', '040 193831', '040 193831', '040 318319', '040 318319',
'040 318319', '040 193831', '040 318319', '041 103194', '041 103194',
'041 310239', '040 193831', '041 103194', '041 310239', '041 310239',
'040 193831', '041 310239', '041 103194', '040 193831', '040 318319']</pre>
<p>Za tako velike številke bi moral imeti seznam zelo veliko predalčkov. Še
huje bi bilo, če bi namesto seznama številk dobili seznam klicanih oseb.</p>
<pre>klici = ['Cilka', 'Dani', 'Berta', 'Dani', 'Ana', 'Berta', 'Berta',
'Berta', 'Dani', 'Dani', 'Dani', 'Dani', 'Dani', 'Berta', 'Berta',
'Berta', 'Dani', 'Berta', 'Cilka', 'Cilka', 'Ana', 'Dani', 'Cilka',
'Ana', 'Ana', 'Dani', 'Ana', 'Cilka', 'Dani', 'Berta']</pre>
<p>Tu smo s seznamom pogoreli. No, ne čisto; rešitev s seznami seveda obstaja,
je pa zoprna - podobna je tistemu, kar smo v začetku počeli z bančnimi
računi.</p>
<p>S slovarji je veliko lepše:
<pre>pogostosti = {}
for ime in klici:
if ime not in pogostosti:
pogostosti[ime] = 0
pogostosti[ime] += 1
print(pogostosti)</pre>
<p>Ob vsakem novem klicu preverimo, ali je klicano <code>ime</code> že v
slovarju. Če ga ni, da dodamo. Nato - najsibo ime novo ali ne - povečamo
števec klicev pri tej osebi.</p>
<p>Ker je ta stvar resnično pogosta, nam Python pomaga z modulom
<code>collections</code>, ki vsebuje podatkovni tip <code>defaultdict</code>.
(Modul, ki vsebuje podatkovni tip?! Da, da; tudi to je ena od stvari, ki jih
bomo našli v modulih.) Ta se obnaša tako kot slovar, z eno izjemo: če zahtevamo
kak element, ki ne obstaja, si ga meni nič tebi nič izmisli. Točneje, doda ga v
slovar in mu postavi privzeto vrednost. Katero, določimo. Pri tem ne podamo
privzete vrednosti, temveč "funkcijo", ki bo vračala privzeto vrednost.
<code>defaultdict</code> bo ustvarjal te, nove vrednosti tako, da bo poklical
to funkcijo brez argumentov in kot privzeto vrednost uporabil, kar vrne
funkcija, ki jo v ta namen vsakič sproti pokliče.</p>
<p>Zelo pogosto bo privzeta vrednost 0 in funkcija, ki vrača
0, se imenuje, hm, <code>int</code>.</p>
<p>("Funkcija" <code>int</code>, je vedno sumljivejša in sumljivejša. Že od
začetka smo v zvezi z njo dajali besedo "funkcija" pod narekovaje, zdaj pa vidimo,
da zmore vedno več in več stvari. Pa še enako ji je ime kot tipu in funkcij, ki
jim je ime enako kot tipom, je vedno več. Kakšna skrivnost se skriva za tem? To
boste izvedeli v enem od prihodnjih napetih nadaljevanj Programiranja 1.)</p>
<p>Preštejmo torej še enkrat Benjaminove klice, tokrat s slovarjem s privzetimi
vrednostmi.</p>
<pre>import collections
pogostosti = collections.defaultdict(int)
for ime in klici:
pogostosti[ime] += 1</pre>
<p>Ni to kul?</p>
<p>Poglejmo si nekaj, kar je kul še bolj.</p>
<h2>Števec</h2>
<p>Preštevanje je pravzaprav tako pogosta reč, da obstaja zanj specializiran
tip. Tako kot <code>defaultdict</code> je v modulu <code>collections</code>,
imenuje pa se <code>Counter</code>.</p>
<pre>>>> stevilo_klicev = collections.Counter(klici)
>>> stevilo_klicev
Counter({'Dani': 11, 'Berta': 9, 'Cilka': 5, 'Ana': 5})</pre>
<p>Komentirali ne bomo veliko, ker še ne znamo. Že ob tem, da sem temu rekel
tip, sem se moral ugrizniti v jezik, saj bi raje govoril o razredu. Kaj je
in kako deluje, pa nas še presega. Zna pa pravzaprav še veliko stvari,
tako da tem, ki jih zanima, priporočam, da si ga ogledajo. Mimogrede:</p>
<pre>>>> napis = "CVIVS IN HOC RENOVATA LOCO PIA FVLGET IMAGO SIS" \
"CVSTOS POPVLI SANCTE IACOBE TVI"
>>> collections.Counter()
Counter({' ': 13, 'I': 8, 'O': 8, 'V': 7, 'A': 6, 'C': 6, 'S': 6, 'T': 5, 'E': 4,
'L': 3, 'N': 3, 'P': 3, 'G': 2, 'B': 1, 'F': 1, 'H': 1, 'M': 1, 'R': 1})</pre>
<p>Se pravi, da lahko kronogram rešimo tudi z</p>
<pre>def kronogram(s):
crke = collections.Counter(s)
return crke["I"] + 5 * crke["V"] + 10 * crke["X"] + 50 * crke["L"] + \
100 * crke["C"] + 500 * crke["D"] + 1000 * crke["M"]</pre>
<h2>Množice</h2>
<p>Množice so podobne seznamom, a s to razliko, da lahko vsebujejo vsak element
samo enkrat. Po drugi strani (in ne le po drugi strani, tudi tehnično) pa
so podobne slovarjem. Niso namreč urejene in vsebujejo lahko le elemente,
ki so nespremenljivi. Poleg tega pa lahko zelo hitro ugotovimo, ali množica
vsebuje določen element ali ne - tako kot lahko pri slovarjih hitro
ugotovimo, ali vsebujejo določen ključ ali ne.</p>
<p>Množice zapišemo z zavitimi oklepaji, tako kot smo vajeni pri matematiki.</p>
<pre>danasnji_klici = {"Ana", "Cilka", "Eva"}</pre>
<p>Tako lahko sestavimo le neprazno množico. Če napišemo le oklepaj in
zaklepaj, <code>{}</code>, pa dobimo slovar. (Čemu so se odločili, naj bo
to slovar, ne množica? Slovar je bil prej, množice je Python dobil kasneje.
Zato.) Če hočemo narediti prazno množico, rečemo</p>
<pre>prazna = set()</pre>
<p>"Funkcija" <code>set</code> je malo podobna "funkciji" <code>int</code>:
damo ji lahko različne argumente, pa jih bo spremenila v množico. Damo ji
lahko, recimo, seznam, pa bomo dobili množico z vsemi elementi, ki se
pojavijo v njem.</p>
<pre>>>> set([1, 2, 3])
{1, 2, 3}
>>> set(range(5))
{0, 1, 2, 3, 4}
>>> set([6, 42, 1, 3, 1, 1, 6])
{1, 42, 3, 6}</pre>
<p>Mimogrede opazimo, da tudi množice, tako kot slovarji, res ne dajo nič na
vrstni red.</p>
<p>Poleg seznamov lahko množicam podtaknemo karkoli, prek česar bi lahko šli
z zanko <code>for</code>, recimo niz ali slovar.</p>
<pre>>>> set("Benjamin")
{'a', 'B', 'e', 'i', 'j', 'm', 'n'}
>>> stevilke
{'Dani': '040 193831', 'Fanči': '040 135367', 'Iva': '040 222333',
'Helga': '+49 175 4728 475', 'Eva': '051 123123', 'Cilka': '041 103194',
'Berta': '040 318319', 'Ana': '041 310239'}
>>> set(stevilke)
{'Iva', 'Helga', 'Eva', 'Berta', 'Fanči', 'Dani', 'Cilka', 'Ana'}</pre>
<p>Spremenljivka <code>stevilke</code> (še vedno) vsebuje slovar, katerega
ključi so imena Benjaminovih oboževalk. Ker zanka prek slovarja "vrača"
ključe, bo tudi množica, ki jo sestavimo iz slovarja, vsebovala ključe.</p>
<p>V množico lahko dodajamo elemente in vprašamo se lahko, ali množica vsebuje
določen element.</p>
<pre>>>> s = set("Benjamin")
>>> "e" in s
True
>>> "u" in s
False
>>> s.add("u")
>>> s
{'a', 'B', 'e', 'i', 'j', 'm', 'n', 'u'}
>>> "u" in s
True
>>> s.add("a")
>>> s.add("a")
>>> s.add("a")
>>> s
{'a', 'B', 'e', 'i', 'j', 'm', 'n', 'u'}</pre>
<p>Na koncu smo poskušali v množico dodati element, ki ga že vsebuje. To seveda
ne gre, množica vsak element vsebuje le enkrat.</p>
<p>Če imamo dve množici, lahko izračunamo njuno unijo, presek, razliko...</p>
<pre>>>> u = {1, 2, 3}
>>> v = {3, 4, 5}
>>> u | v
{1, 2, 3, 4, 5}
>>> u & v
{3}</pre>
<p>Preverimo lahko tudi, ali je neka množica podmnožica (ali nadmnožica druge).
To najpreprosteje storimo kar z operatorji za primerjanje.</p>
<pre>>>> u = {1, 2, 3}
>>> {1, 2} <= u
True
>>> {1, 2, 3, 4} <= u
False
>>> {1, 2, 3} <= u
True
>>> {1, 2, 3} < u
False</pre>
<p><code>{1, 2, 3}</code>, je podmnožica <code>u</code>-ja, ni pa njegove
<em>prava podmnožica</em>, saj vsebuje kar cel <code>u</code>.</p>
<p>Z množicami je mogoče početi še marsikaj zanimivega - vendar bodi dovolj.</p>
<h4>Primer: Seznami v slovarjih</h4>
<p>Recimo, da želimo sestaviti skupine datotek v nekem direktoriju glede na
končnice. Sestavili bomo slovar, katerega ključi bodo končnice, na primer
.py, .txt in .mp3, vrednosti pri vsakem ključu pa imena datotek s to
končnico.</p>
<pre>datoteke = {}
for ime in os.listdir(dir):
konc = os.path.splitext(ime)[1]
if not konc in datoteke:
datoteke[konc] = set()
datoteke[konc].add(ime)</pre>
<p>Če ste bili pozorni, niste spregledali, da je gornji program pravzaprav na
nek način enak programu, s katerim smo preštevali, kolikokrat je Benjamin
klical katero od deklet:
<pre>pogostosti = {}
for ime in klici:
if not ime in pogostosti:
pogostosti[ime] = 0
pogostosti[ime] += 1</pre>
</p>
<p>"Istost" je v tem, da obakrat naredimo prazen slovar. Nato gremo z zanko
<code>for</code> čez neke reči. Za vsako reč (v gornjem primeru datoteke, v
spodnjem ime dekleta) preverimo, ali je že v slovarju. Če je ni, jo dodamo tako,
da ji damo neko privzeto vrednost (zgoraj prazno množico, spodaj 0). Nato
pravkar dodano stvar posodobimo glede na trenutno reč (v gornjem primeru dodamo
datoteko v pravkar sestavljeno ali od prej obstoječo množico, v spodnjem povečamo
število klicev te osebe za 1).</p>
<p>Aja, to finto pa že poznamo. Slovarji s privzetimi vrednostmi!</p>
<pre>datoteke = collections.defaultdict(set)
for ime in os.listdir(dir):
konc = os.path.splitext(ime)[1]
datoteke[konc].add(ime)</pre>
</html>
|