Elektronski naslovi
Naloga
Elektronski naslovi
V teh domači nalogi boste morali
- ločiti med terkami, seznami, slovarji in množicami,
- razumeti funkcije, kar vam bo pomagalo plavati čez štirinajst dni,
- znati delati z datotekami in nizi.
Tokrat izjemoma(?) ne bo ogrevalnih, obveznih in dodatnih nalog, ker sem nalogo razdrobil čisto nadrobno (da bi bolj pridno pisali funkcije) in ker je vse dovolj preprosto, da ničemur ne bi rekel "dodatno". Vse je torej obvezno.
V nalogi bomo delali z datotekami, v katerih so imena in priimki ter elektronski naslovi. Tipična vrstica bo torej
Janez Novak janez.novak@gmail.com
pri čemer je med imenom-in-priimkom in elektronskim naslovom tabulator
(\t). Ko boste brali datoteko, bo na koncu vrstice navadno znak za
prehod v novo vrstico, \n.
Razčleni
Napiši funkcijo razcleni, ki prejme niz, kakor je zgornji:
sestavljen je iz dveh delov, ki sta ločena s tabulatorjem. V prvem delu sta
priimek in ime, v drugem naslov. Funkcija naj vrne terko z dvema elementoma -
prvi je niz z imenom in priimkom, drugi elektronski naslov. Tako mora, recimo,
klic razcleni("Janez Novak\tjanez.novak@gmail.com") vrniti terko
("Janez Novak", "janez.novak@gmail.com").
Iz obeh delov izločite vse morebitne odvečne presledke ali znake za novo vrstico na začetku ali koncu.
Poleg tega poskrbite za primerke, ki nočejo povedati svojega naslova: če
naslova ni ali če naslov ne vsebuje znaka @, naj funkcija namesto
naslova vrne prazen niz. To naj se zgodi, če pokličemo
razcleni("Janez Novak\tne-povem-naslova") ali
razcleni("Janez Novak\t") ali
razcleni("Janez Novak"). Popazite na vse tri primere.
Končno, lahko se zgodi, da je vrstica prazna oz. vsebuje same presledke ali kaj podobnega. V tem primeru vrnite dva prazna niza, saj nimamo ne imena ne naslova.
Funkcija ni zapletena - samo dva if-a, par
return-ov in par klicev metod strip in
split. Je pa, zaradi vseh teh pogojev najdaljša (glava + šest
vrstic).
Ponudnik
Napiši funkcijo ponudnik(naslov), ki sprejme elektronski naslov
in vrne "ponudnika" naslova. Če dobi kot argument, recimo, niz
"janez.novak@gmail.com", mora vrniti "gmail.com"; če
dobi "janez.novak@pef.uni-lj.si", naj vrne
"pef.uni-lj.si".
Predpostaviti smete, da vsebuje niz natančno en znak @. (Da, rešitev je čisto kratka, funkcija ima eno vrstico, poleg glave.)
Branje naslovov
Napiši funkcijo preberi_naslove(datoteka), ki kot argument dobi
ime datoteke, kot rezultat pa vrne slovar, katerega ključi so
elektronski naslovi, vrednosti pa imena in priimki lastnikov.
Če ji damo, recimo, datoteko
Janez Novak janez.novak@gmail.com
Peter Piker peter.piker@hotmail.com
mora vrniti slovar {"janez.novak@gmail.com": "Janez Novak",
"peter.piker@hotmail.com": "Peter Piker"}.
Datoteko odprite z open(datoteka, encoding="utf-8") in jo
berite z zanko for. Če ne znate, poglejte
zapiske.
Funkcija naj ignorira vse vrstice, v katerih ni elektronskih naslovov.
Obstajajo osebe, ki imajo več naslovov. To naj te ne moti: naslovi, ki jih boš uporabil za ključe, so unikatni. Vrednosti se bodo ponavljale, a slovarjev to ne moti.
Namig: stvar je preprosta. Narediš prazen slovar, bereš datoteko, "razčleniš" vsako vrstico in če vsebuje naslov, dodaš v slovar pod ključem naslov ime. Najbrž šest vrstic.
Naslovi po ponudnikih
Napiši funkcijo po_ponudnikih(naslovi), ki kot argument prejme
slovar, kakršnega vrača funkcija preberi_naslove (torej: v tej
funkciji ne boste poklicali preberi_naslove, temveč jo bo poklical
nekdo drug in vam dal rezultat tega klica). Kot rezultat naj vrne slovar,
katerega ključi so ponudniki, vrednosti pa množice elektronskih naslovov pri tem
ponudniku. Za testne naslove mora vrniti
Pazite: gre za slovar, v njem pa so množice (kot vrednosti - ključi pa so nizi). Menda smo imeli nek podoben primer tudi na predavanjih.
Namig: defaultdict. Funkcija sme vrniti tudi
defaultdict namesto običajnega slovarja.
Naslovi po ljudeh
Napiši funkcijo po_ljudeh(naslovi), ki prejme naslove v obliki,
kot jih vrača preberi_naslove, in vrne slovar, katerega ključi so
imena, vrednosti pa množice vse naslovov te osebe. Za
testne naslove mora vrniti
Najpopularnejši
Napišite funkcijo najpopularnejsi(s), ki prejme takšen slovar,
kakršnega vračata prejšnji dve funkciji (ena ali druga - oba imata za ključe
nize, za vrednosti pa množice, le vsebine teh nizov in množic so druge).
Če ji damo slovar po ponudnikih, naj funkcija vrne ponudnika, ki ima največ
strank (v gornjem primeru mora vrniti "gmail.com". Če ji damo
slovar po ljudeh, mora vrniti človeka, ki ima največ naslovo (v gornjem primeru
je to "Peter Petrovic".
Namig: delaj se, da delaš prvo, pa boš videl, da bo funkcija "slučajno" zmogla tudi drugo.
Tole je kar poučna naloga, zato jo napiši lepo: funkcija ima glavo in pet vrstic. Vse, kar je več, je odveč.
Skupne stranke
Napiši funkcijo skupne_stranke(ponudniki, ponudnik1, ponudnik2),
ki prejme slovar, kakršnega vrača funkcija po_ponudnikih in imeni
dveh ponudnikov. Vrne naj množico vseh oseb, ki imajo naslove pri obeh podanih
ponudnikih. V gornjem primeru je to Peter Petrovic. (Po domače: računamo presek
gmailovih in hogmailovih strank.
Podobnost ponudnikov
Podobnost med dvema ponudnika lahko definiramo kot presek njunih strank deljen z unijo njunih strank. Če imata dva operaterja pet skupnih strank, vseh njunih strank skupaj pa je 20 (s tem, da tiste, ki se pojavijo pri obeh, štejemo samo enkrat), je podobnost med njima 5/20.
Napiši funkcijo jaccard(ponudniki, ponudnik1, ponudnik2).
Funkcija prejme enake argumente kot prejšnja funkcija, rezultat pa naj bo
podobnost med ponudnikoma.
Predpostaviti smeš, da ima vsaj en ponudnik vsaj eno stranko.
Rešitev
Razčleni
Pri funkcijah, kakršna je razcleni, se nam vedno splača malo
razmisliti. Včasih bomo tako že takoj napisali preprosto funkcijo, včasih pa
bomo najprej sprogramirali nekaj daljšega in potem poenostavljali.
Takole gre: pokličemo strip in split. Če je
rezultat splita dolg le en element (v vrstici ni bilo tabulatorja
ali pa je bilo za tabulatorjem vse prazno in ga je še pred splitom
odstranil strip) bomo vrnili prvi element in prazen niz.
Če sicer dobimo dva elementa, vendar drugi ne vsebuje znaka @, prav tako vrnemo prvi element in prazen niz. Oba pogoja lahko kar združimo: če imamo manj kot dva elementa ali pa drugi ne vsebuje @, vrnemo prvi element in prazen niz. To mimogrede poskrbi še za primer, ko bi dobili samo prazno vrstico - v tem primeru bo prvi (in edini) element prazen niz, pa bomo lepo vrnili dva prazna niza.
Če do gornih nevšečnosti ne pride, pa vrnemo oba elementa, pred čemer
pretvorimo seznam, ki ga je vrnil split, v terko
(tuple).
Ponudnik
Tole je preprosto. Nekateri ste si zagrenili življenje z nepotrebnim
find; tudi tule je split priročnejši.
Preberi naslove
Tule le lepo beremo datoteko, vsako vrstico pošljemo v funkcijo
razcleni in če drugi element rezultata, naslov,
ni prazen niz, shranimo naslov in ime v slovar.
Tisti, ki se bojite klicati lastne funkcije, ste znotraj zanke ponavljali
kodo, ki ste jo napisali že v funkciji razcleni. To ni dobro;
eden od glavnih ciljev te naloge je bil, da se znebite straha pred klicanjem
lastnih funkcij.
Po ljudeh
Tole je precej po zgledu natakarja, s katerim smo se igrali na predavanjih. Spet naredimo slovar, v katerem bodo vrednosti množice, le da tokrat vanj ne zlagamo naročil, ki jih dobimo v nekem seznamu, temveč podatke, ki jih beremo iz slovarja.
Zanko se splača napisati v obliki
for naslov, ime in naslovi.items(). Tako iz slovarja dobimo vse
pare (naslov, ime). Za vsako ime moramo
dodati v pripadajočo množico naslov.
Po ponudnikih
In tole je ravno obratno: za vsak naslov shranimo ime ... le da ne za vsak
naslov temveč za vsakega "ponudnika", ki ga dobimo tako, da pokličemo funkcijo
ponudnik, ki smo jo napisali zgoraj.
Najpopularnejši
Spet gremo z zanko prek slovarja, znotraj nje pa počnemo, kar smo že velikokrat: včasih smo po seznamih iskali največje število, najdaljše ime... tokrat pa iščemo največjo množico (vrednost) in ključ, ki ji pripada.
najvec bo velikost največje množice doslej; v začetku jo
postavimo na 0. Potem gremo prek slovarja in ko naletimo na večjo množico od
največje doslej, si zapomnimo, za katerega ponudnika (oz. za katerega človeka)
gre (naj_k) in koliko strank (oz. naslovov) ima.
Skupne stranke
Naloga je trivialna za vse, ki ste si zapomnili, kaj se da početi z množicami (v Pythonu in tudi sicer). Sprašuje namreč po velikosti preseka dveh množic.
Jaccard
... je pa isto, samo da velikost preseka delimo še z velikostjo unije.