Rješenja nekih zadataka, koje ste rješavali u C-u, pomoću pythona

Zadaća 1 (map(), lambda, math.sin(), sum)

Zadatak 3b

import math
N = input()
L = range(0,N+1)
L = map( lambda x: x*math.pi/float(N), L )
L = map( math.sin, L )
print sum(L)

Pokrenemo program i unesemo npr. 10, rezultat je:

6.31375151468

U interaktivnom modu možemo pratiti izvršavanje:

>>> import math
>>> N = 10
>>> L = range(0,N+1)
>>> L
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> L = map( lambda x: x*math.pi/10., L )
>>> 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 = map( math.sin, L )
>>> 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.31375151468

Zadaća 2 ([ ... for ... in ...], string.join())

Zadatak 1b

L = [x * 0.1 for x in range(31,41)]

print "Tablica mnozenja 1b:"
for i in L:
  print "%5.2f" % (i*L[0]),
  for j in L[1:]:
    print "|%5.2f" % (i*j),
  print
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

Naredba print zajedno sa zarezom omogućuje da ostanemo u istom retku ali ostavlja neželjeni razmak. Rješenje se može naći tražeći python print without newline space na google-u, i to je koristiti 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
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

Za rješavanje ovog zadatka može poslužiti funkcija string.join().

print "-između-".join(["100","200"," ","abc"])
100-između-200-između- -između-abc

Poč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)
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

Zadaća 3 (zip, input, math.sqrt())

Zadatak 4

Promotrimo ovaj primjer:

>>> 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 = input()
a = []
b = []
for i in range(0,N):
  a.append( input() )

for i in range(0,N):
  b.append( 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.707106781187

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 funkcije id(), ali na adrese ne možemo upisivati direktno.]

Zadaća 5 (string.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 modula itertools. 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 i unrle.py.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

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 python
# -*- coding: utf-8 -*-

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 +x rle.py
$ chmod +x unrle.py

Kad datoteku z6src.py

aaaabbbbaaaaccccdef

komprimiramo

$ ./rle.py z6src.txt z6out.txt

dobijemo

4a4b4a4c1d1e1f

Kad taj rezultat dekomprimiramo

$ ./unrle.py z6out.txt z6out2.txt

dobijemo nazad početni sadržaj

aaaabbbbaaaaccccdef

Naravno, 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 python
# -*- coding: utf-8 -*-

import sys
import struct
import os

def isprint(c):
  return 0x20<=ord(c)<=0x7e

def konverzija(c):
  if isprint(c):
    return c
  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 >>sys.stderr, "s pozicije %d procitati %d procitano %d. " % (P,duljina,len(byteovi)), repr(byteovi)
  assert len(byteovi) == duljina

vrijednosti = struct.unpack( fmt, byteovi )
if T == "c":
  vrijednosti = map( konverzija, vrijednosti )
  s = "".join(vrijednosti)
else:
  vrijednosti = map( str, vrijednosti )
  s = " ".join(vrijednosti)

print s

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 +x rr.py
./rr.py bin1.bin 0 10 c
./rr.py bin1.bin 12 1 d
./rr.py bin1.bin 16 1 lf
tekst[00][00][00][00][00]
10
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 jednostavno koje je malo nepotpuno, a zatim potpuno rješenje.

Za obradu grešaka je u pythonu 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 python
# -*- coding: utf-8 -*-

import sys

def readfile( fn ):
  with open( fn, "r" ) as f:
    s = f.read()
  return s

try:
  p = readfile( sys.argv[1] )
  print "datoteka je uspjesno procitana, i sadrzi %d byteova" % len(p)
  errcode = 0
except Exception as e:
  print "greska ", e
  errcode = 1

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 python
# -*- coding: utf-8 -*-

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, "r" )
    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 "datoteka je uspjesno procitana, i sadrzi %d byteova" % len(p)
  errcode = 0
except GrOtvaranje as e:
  print "greska prilikom otvaranja datoteke", e
  errcode = 1
except GrRezMem as e:
  print "greska prilikom rezerviranja memorije", e
  errcode = 2
except GrCitanje as e:
  print "greska prilikom citanja datoteke", e
  errcode = 3
except GrNepoznata as e:
  print "nepoznata greska u readfile", e
  errcode = 4
except:
  print "neocekivana greska", 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
./readfile2.py bin1.bin
datoteka je uspjesno procitana, i sadrzi 24 byteova
datoteka je uspjesno procitana, i sadrzi 24 byteova

Isprobajmo kako rade kad im se zada pogrešno ime datoteke odnosno ime datoteke koja ne postoji:

./readfile.py nepostojeca.datoteka
echo $?
./readfile2.py nepostojeca.datoteka
echo $?
greska  [Errno 2] No such file or directory: 'nepostojeca.datoteka'
1
greska 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 $?
./readfile2.py bin1.bin
echo $?
chmod u+r bin1.bin
greska  [Errno 13] Permission denied: 'bin1.bin'
1
greska prilikom otvaranja datoteke [Errno 13] Permission denied: 'bin1.bin'
1