Rješenja nekih zadataka, koje ste rješavali u C-u, pomoću pythona¶
Ideja je u pythonu riješiti zadatke koje ste već rješavali u C-u i time produbiti znanje pythona.
Zadaća 1 (map()
, lambda
, math.sin()
, sum
, [ ... for ... in ...], ( ... for ... in ...) )¶
Zadatak 3b¶
import math N = int(input()) L = range(0,N+1) L = (x*math.pi/float(N) for x in L) L = map( math.sin, L ) print(sum(L))Pokrenemo program i unesemo npr.
10
, rezultat je:6.313751514675044U interaktivnom modu možemo pratiti izvršavanje:
>>> import math >>> N = 10 >>> L = list(range(0,N+1)) >>> L = [x*math.pi/10. for x in L] >>> list(map( lambda x: "%.2f" % x, L )) # ispis samo 2 decimale ['0.00', '0.31', '0.63', '0.94', '1.26', '1.57', '1.88', '2.20', '2.51', '2.83', '3.14'] >>> L = list(map( math.sin, L )) >>> list(map( lambda x: "%.2f" % x, L )) # ispis samo 2 decimale ['0.00', '0.31', '0.59', '0.81', '0.95', '1.00', '0.95', '0.81', '0.59', '0.31', '0.00'] >>> sum(L) 6.313751514675044Pozor
Za razliku od samostalnog programa gdje smo svaki međurezultat koristili samo jednom u interaktivnom primjeru smo npr.
L
iz 4. retka koristili jednom u 5. a drugi put u 6. retku. To je važno uočiti jer u slučaju da nam neka lista treba samo jednom možemo koristiti tzv. iteratore koji ne zauzimaju toliko memorije koliko liste i računaju se tek kad zatreba. Mana iteratora je da ih se može koristiti samo jednom. Također treba uočiti da je rezultat odmap()
iterator, kao i rezultat od( ... for ... in ...)
. Naprotiv, rezultat od[ ... for ... in ...]
je lista. Više o generatorima u odjeljku Generatori/iteratori.Kako iteratore možemo upotrijebiti samo jednom, u drugom primjeru smo morali koristiti
list()
i pretvoriti iteratore u liste da bismo ih mogli koristiti više puta.
Zadaća 2 (str.join()
, *args
)¶
Zadatak 1b¶
Rješenje 1¶
L = [x * 0.1 for x in range(31,41)] print("Tablica mnozenja 1b:") for i in L: print("%5.2f" % (i*L[0]), end='') for j in L[1:]: print("|%5.2f" % (i*j), end='') print()
Rješenje 2¶
Moguće je koristiti direktan upis stringa u stdout
pomoću sys.stdout.write
.
import sys L = [x * 0.1 for x in range(31,41)] print("Tablica mnozenja 1b:") for i in L: sys.stdout.write( "%5.2f" % (i*L[0]) ) for j in L[1:]: sys.stdout.write( "|%5.2f" % (i*j) ) print()
Rješenje 3¶
Za rješavanje ovog zadatka može poslužiti funkcija
str.join()
.print("-između-".join(["100","200"," ","abc"]))100-između-200-između- -između-abcPočnemo tako da generiramo tablicu množenja kao dvodimenzionalnu listu
L2
.L = [x * 0.1 for x in range(31,41)] L2 = [["%5.2f"%(i*j) for j in L] for i in L] print(L2[0]) print("...") print(L2[-1])[' 9.61', ' 9.92', '10.23', '10.54', '10.85', '11.16', '11.47', '11.78', '12.09', '12.40'] ... ['12.40', '12.80', '13.20', '13.60', '14.00', '14.40', '14.80', '15.20', '15.60', '16.00']Zatim povežemo unutarnju listu znakom
"|"
, a vanjsku listu znakom za novi red"\n"
.L = [x * 0.1 for x in range(31,41)] L2 = ["|".join(["%5.2f"%(i*j) for j in L]) for i in L] print("Tablica mnozenja 1b:") print("\n".join(L2))
Rješenje 4¶
L = [1,2,3] print(L) print(*L) print(*L,sep='//')[1, 2, 3] 1 2 3 1//2//3Vidimo da ako je
L=[1,2,3]
tada jeprint(L)
isto što iprint([1,2,3])
(ispis liste), aprint(*L)
isto što iprint(1,2,3)
(ispis 3 broja).L = [x * 0.1 for x in range(31,41)] print("Tablica mnozenja 1b:") for x in L: print( *["%5.2f" % (x*y) for y in L], sep='|' )Tablica mnozenja 1b: 9.61| 9.92|10.23|10.54|10.85|11.16|11.47|11.78|12.09|12.40 9.92|10.24|10.56|10.88|11.20|11.52|11.84|12.16|12.48|12.80 10.23|10.56|10.89|11.22|11.55|11.88|12.21|12.54|12.87|13.20 10.54|10.88|11.22|11.56|11.90|12.24|12.58|12.92|13.26|13.60 10.85|11.20|11.55|11.90|12.25|12.60|12.95|13.30|13.65|14.00 11.16|11.52|11.88|12.24|12.60|12.96|13.32|13.68|14.04|14.40 11.47|11.84|12.21|12.58|12.95|13.32|13.69|14.06|14.43|14.80 11.78|12.16|12.54|12.92|13.30|13.68|14.06|14.44|14.82|15.20 12.09|12.48|12.87|13.26|13.65|14.04|14.43|14.82|15.21|15.60 12.40|12.80|13.20|13.60|14.00|14.40|14.80|15.20|15.60|16.00
Više o pozivima funkcija, *args
i **kwargs
u odjeljku Pozivanje funkcija.
Zadaća 3 (zip
, input
, math.sqrt()
)¶
Zadatak 4¶
Promotrimo ovaj primjer:
>>> list(zip( [100,200], [3,4] )) [(100, 3), (200, 4)] >>> [i+j for i,j in zip([100,200],[3,4])] [103, 204]Vidimo kako je moguće jednostavno raditi operacije nad dvjema listama, tako da se uzima prvi (100) s prvim (3), drugi (200) s drugim (4), itd. To iskoristimo za rješavanje zadatka.
import math N = int(input()) a = [] b = [] for i in range(0,N): a.append( float(input()) ) for i in range(0,N): b.append( float(input()) ) ab = (i*j for i,j in zip(a,b)) a2 = (i*i for i in a) b2 = (i*i for i in b) ab = sum(ab) a2 = math.sqrt(sum(a2)) b2 = math.sqrt(sum(b2)) print("cos(theta) je", ab/a2/b2)Unesemo
2 1 0 1 1
kao input. Rezultat je:cos(theta) je 0.7071067811865475
Zadaća 4¶
Problemi opisani u zadaći 4 ne javljaju se u pythonu jer u pythonu “nema” pointera, a i provjerava se da indeks polja bude unutar trenutno dopuštenog opsega. [U CPythonu možemo doznati adresu pomoću funkcijeid()
, ali na adrese ne možemo upisivati direktno.]
Zadaća 5 (str.replace()
)¶
Zadatak 3¶
Trivijalno jer python podržava rad s proizvoljno dugim cijelim brojevima.
Zadatak 4¶
Trivijalno jer već postoji takva funkcija u pythonu.
print("1456".replace("1","123"))123456
Zadaća 6 (itertools.groupby()
)¶
Zadaci 2a i 2b¶
Za rješavanje ovog zadatka dobro može poslužiti funkcija
groupby
iz modulaitertools
. Ta funkcija razdvaja listu ili string na dijelove:>>> import itertools >>> [k for k,v in itertools.groupby("aaabbcd")] ['a', 'b', 'c', 'd'] >>> [list(v) for k,v in itertools.groupby("aaabbcd")] [['a', 'a', 'a'], ['b', 'b'], ['c'], ['d']] >>> [len(list(v)) for k,v in itertools.groupby("aaabbcd")] [3, 2, 1, 1] >>> [k*len(list(v)) for k,v in itertools.groupby("aaabbcd")] ['aaa', 'bb', 'c', 'd']Koristeći to, lako je napraviti
rle.py
iunrle.py
.#!/usr/bin/env python3 import sys import itertools def rle(src,dest): str = src.read() str = "".join( ["%d%s" % (len(list(v)),k) for k,v in itertools.groupby(str)] ) dest.write(str) with open(sys.argv[1],"r") as src: with open(sys.argv[2],"w") as dest: rle(src,dest)#!/usr/bin/env python3 import sys def unrle(src,dest): str = src.read() assert len(str) % 2 == 0 ponavljanja = str[0::2] #svaki drugi parni ponavljanja = map(int,ponavljanja) znakovi = str[1::2] #svaki drugi neparni str = [z*p for z,p in zip(znakovi,ponavljanja)] str = "".join(str) dest.write(str) with open(sys.argv[1],"r") as src: with open(sys.argv[2],"w") as dest: unrle(src,dest)Da bi se ti programi mogli pokretati kao samostalni, potrebno je dodati pravo izvršavanja.
$ chmod u+x rle.py $ chmod u+x unrle.pyKad datoteku
z6src.py
aaaabbbbaaaaccccdefkomprimiramo
$ ./rle.py z6src.txt z6out.txtdobijemo
4a4b4a4c1d1e1fKad taj rezultat dekomprimiramo
$ ./unrle.py z6out.txt z6out2.txtdobijemo nazad početni sadržaj
aaaabbbbaaaaccccdefNaravno, ovdje se pretpostavljalo, kako je rečeno u zadatku, da se znakovi neće ponavljati više od 9 puta.
Zadaća 7¶
Zadatak 2¶
Rješenje je:
#!/usr/bin/env python3 import sys import struct import os def isprint(c): return 0x20<=ord(c)<=0x7e def konverzija(c): if isprint(c): return c.decode('ascii') else: return "[%02x]" % ord(c) D, P, N, T = sys.argv[1:5] P = int(P) N = int(N) rjecnik = {} rjecnik["c"] = "c" rjecnik["d"] = "i" rjecnik["ld"] = "l" rjecnik["f"] = "f" rjecnik["lf"] = "d" with open(D,"rb") as f: fmt = '%d%s' % (N,rjecnik[T]) duljina = struct.calcsize(fmt) f.seek( P, os.SEEK_SET ) byteovi = f.read( duljina ) #print(f"s pozicije {P} procitati {duljina} procitano {len(byteovi)}:", repr(byteovi), file=sys.stderr) assert len(byteovi) == duljina vrijednosti = struct.unpack( fmt, byteovi ) if T == "c": print( *map( konverzija, vrijednosti ), sep='' ) else: print( *vrijednosti )
Tu smo koristili pythonov dictionary i time smo izbjegli potrebu za velikim brojem if-ova. Funkcija isprint
ne postoji u pythonu, ali možemo pogledati kako je napravljena u C-u pa taj kôd prevesti u python. Gornju datoteku nazovemo rr.py
.
Krećemo od datoteke iz jednog od prethodnih primjera:
$ hexdump -C bin1.bin 00000000 74 65 6b 73 74 00 00 00 00 00 00 00 0a 00 00 00 |tekst...........| 00000010 9a 99 99 99 99 99 b9 3f |.......?| 00000018
Kad postavimo flag izvršavanja datoteci rr.py
možemo radi testiranja pokrenuti program nad podacima u bin1.bin
.
$ chmod u+x rr.py $ ./rr.py bin1.bin 0 10 c tekst[00][00][00][00][00] $ ./rr.py bin1.bin 12 1 d 10 $ ./rr.py bin1.bin 16 1 lf 0.1
Zaključujemo da postoji prazan prostor (zbog alignmenta) u byteovima redni broj 10 i 11.
Zadaća 8 (try
... except
... else
, raise
)¶
Zadatak 2¶
U pythonu postoji funkcija read
slična traženoj. Python se brine oko dealokacije. Duljina N
nam nije potrebna jer uvijek možemo koristiti len
. Prikazat ćemo dva rješenja: prvo nepotpuno ali iznimno jednostavno, a zatim, drugo, potpuno rješenje.
Za obradu grešaka u pythonu je prirodno koristiti tzv. try..except
konstrukciju. Izvršavanje krene u try
blok i izvršava se red po red ali ako dođe do greške odmah se nepovratno napušta try
blok i prelazi u prvi kompatibilan except
blok. (U except
blok se ulazi samo ako se dogodi greška u try
bloku koji mu prethodi. U else
blok se ulazi samo ako se čitav prethodni try
blok izvršio bez greške.)
#!/usr/bin/env python3 import sys def readfile( fn ): with open( fn, "rb" ) as f: s = f.read() return s try: p = readfile( sys.argv[1] ) except Exception as e: print("greška ", e) errcode = 1 else: print(f"datoteka je uspješno pročitana, i sadrži {len(p)} byteova") errcode = 0 sys.exit(errcode)
Ako bismo baš htjeli da povratne vrijednosti budu diferencirane kao što se traži u zadatku potrebno je dodati još provjera.
#!/usr/bin/env python3 import sys class Gr(Exception): pass class GrOtvaranje(Gr): pass class GrRezMem(Gr): pass class GrCitanje(Gr): pass class GrNepoznata(Gr): pass def readfile( fn ): try: try: f = open( fn, "rb" ) except IOError as e: raise GrOtvaranje(e) else: try: with f: s = f.read() except IOError as e: raise GrCitanje(e) except MemoryError as e: raise GrRezMem(e) except Gr as e: raise except Exception as e: raise GrNepoznata(e) return s try: p = readfile( sys.argv[1] ) print(f"datoteka je uspješno pročitana, i sadrži {len(p)} byteova") errcode = 0 except GrOtvaranje as e: print("greška prilikom otvaranja datoteke", e) errcode = 1 except GrRezMem as e: print("greška prilikom rezerviranja memorije", e) errcode = 2 except GrCitanje as e: print("greška prilikom citanja datoteke", e) errcode = 3 except GrNepoznata as e: print("nepoznata greška u readfile", e) errcode = 4 except: print("neočekivana greška", sys.exc_info()) errcode = 5 sys.exit(errcode)
Isprobajmo sad oba primjera. Pogledajmo output sljedećih naredbi upisanih u terminal:
$ chmod +x readfile.py $ chmod +x readfile2.py $ ./readfile.py bin1.bin; echo $? datoteka je uspješno pročitana, i sadrži 24 byteova 0 $ ./readfile2.py bin1.bin; echo $? datoteka je uspješno pročitana, i sadrži 24 byteova 0
Isprobajmo kako rade kad im se zada pogrešno ime datoteke odnosno ime datoteke koja ne postoji:
$ ./readfile.py nepostojeca.datoteka; echo $? greška [Errno 2] No such file or directory: 'nepostojeca.datoteka' 1 $ ./readfile2.py nepostojeca.datoteka; echo $? greška prilikom otvaranja datoteke [Errno 2] No such file or directory: 'nepostojeca.datoteka' 1
Isprobajmo što se događa u slučaju kad im se zada ime postojeće datoteke za koju je ukinuto pravo čitanja:
$ chmod u-r bin1.bin $ ./readfile.py bin1.bin; echo $? greška [Errno 13] Permission denied: 'bin1.bin' 1 $ ./readfile2.py bin1.bin; echo $? greška prilikom otvaranja datoteke [Errno 13] Permission denied: 'bin1.bin' 1 $ chmod u+r bin1.bin
Vidimo da oba programa ispravno obrađuju greške. Poanta je da koristeći try/except
lako možemo postići osnovnu obradu grešaka kao u prvom primjeru, te po potrebi nadograđivati kao u drugom primjeru.