Sunteți pe pagina 1din 195

Fundamentele Programrii

Limbajul de programare
PYTHON
Ce este programarea
Hardware / software
Hardware - computere(desktop,laptop, etc) i alte dispozitive (mobile, atm,
etc)
Software - programe sau sisteme ce ruleaz pe hardware
Limbaj de programare Notaii i reguli pentru scrierea de programe
(sintax i semantic)
Python: Limbaj de programare de nivel nalt (high level programming
language).
Interpretor Python: un program care permite rularea/interpretarea
programelor scrise in limbajul Python.
Biblioteci Python: Funcii, module, tipuri de date disponibile n Python,
scrise de ali programatori

Program 1 - Hello world

print (Hello world)


Ce fac computerele
Stocheaz date
Memoria intern
Memoria extern (hard, stick, CD, etc)
Opereaz
procesor
Comunic
Prin tastatur, mouse, ecran
Conexiuni de tip reea

Informaii i date
Date - o colecie de simboluri stocate ntr-un computer (Ex. 123 decimal
sau irul de caractere abc) sunt stochate folosind reprezentarea binara
Informaii - interpretarea unor date (Ex. 123, abc)
Procesarea datelor i informaiilor
Dispozitivele de intrare transform informaiile n date (ex. 123
citit de la tastatur)
Datele sunt stocate n memorie (ex. 1111011 pentru numrul
123)
Dispozitivele de ieire produc informaii din date
Operaii de baz ale procesoarelor
n reprezentare binar
Operaii (and, or, not; add, etc)
Elemente de baz ale unui program Python
Program 2 - Adding two integers

# Reads two integers and prints the sum of them


a = input("Enter the first number: ")
b = input("Enter the second number: ")
c = int(a) + int(b)
print("The sum of ", a, " + ", b, " is ", c)

Elemente lexicale
Un program Python este alctuit din mai multe linii de cod
Comentarii
ncep cu # i in pn la sfritul liniei

ncep cu i in mai multe rnduri, pn la un nou

Identificatori:secvene de caractere (litere, cifre, _) care ncep cu o liter


sau cu _

Literali: notaii pentru valorile constante sau pentru tipuri definite de


utilizator
Modelul de date
Toate datele ntr-un program Python obiecte
Un obiect are :
o identitate adresa lui n memorie
un tip care determin operaiile posibile precum i valorile pe
care le poate lua obiectul
o valoare.

Odat creat, identitatea i tipul obiectului nu mai pot fi


modificate.
Valoarea unor obiecte se poate modifica
Obiecte mutabile - se poate modifica
Obiecte ne-mutabile nu se poate modifica
Tipuri de date standard
Tipul de date definete domeniul de valori posibile i operaiile permise asupra
valorilor din domeniu.

Numerice Numerele sunt inmutabile odat create valoare nu se mai poate


schimba (operaiile creaz noi obiecte).

int (numere ntregi):


numerele ntregi (pozitive i negative), dimensiune limitat doar
de memoria disponibil
Operaii: +, -, *, /, //, **, % comparare:==,!=,<, > operaii pe bii: |,
^, &, <<, >>, ~
Literali: 1, -3

bool (boolean):
Valorile True i False.
Operaii: and, or, not
Literali: False, True; 0, 1

float (numere reale):


numerele reale (dubl precizie)
Operations: +, -, *, / comparare:==,!=,<, >
Literals: 3.14
Tipuri de date standard

Secvene:
Mulimi finite i ordonate, indexate prin numere ne-negative.
Dac a este o secven atunci:
len(a) returnez numrul de elemente;
a[0], a[1], , a[len(a)-1] elementele lui a.
Examples: [1, a]
Stringuri:
este o secven inmutabil;
caractere Unicode .
Literali: abc, abc

Liste
secven mutabil
ex: [] sau [1, a, [1, 3]]
Liste
operaii:
creare [7, 9]
accesare valori,lungime (index, len), modificare valori (listele sunt mutabile),
verificare daca un element este in lista (2 in [1, 2, 'a'])
stergere inserare valori (append,insert,pop) del a[3]
slicing, liste eterogene
listele se pot folosi in for
lista ca stiv(append, pop)
folositi instructiunea help(list) pentru mai multe detalii despre operaii posibile

# create # slicing
a = [1, 2, 'a'] print a[:2]
print (a) b = a[:]
x, y, z = a print (b)
print(x, y, z) b[1] = 5
print (b)
# indices: 0, 1, ..., len(a) 1 a[3:] = [7, 9]
print a[0] print(a)
print ('last element = ', a[len(a)-1]) a[:0] = [-1]
print(a)
# lists are mutable a[0:2] = [-10, 10]
a[1] = 3 print(a)
print a
# lists as stacks # nesting
stack = [1, 2, 3] c = [1, b, 9]
stack.append(4) print (c)
print stack
print stack.pop()
print stack
#generate lists using range #list in a for loop
l1 = range(10) l = range(0,10)
print l1 for i in l:
l2 = range(0,10) print i
print l2
l3 = range(0,10,2)
print l3
l4 = range(9,0,-1)
print l4
Tuple
Sunt secvene inmutabile. Conine elemente, indexat de la 0
Operations:
Crearea - packing (23, 32, 3)
eterogen
poate fi folosit in for
unpacking

# Tuples are immutable sequences # tuple with one item


# A tuple consists of a number of singleton = (12,)
values separated by commas print (singleton)
print (len(singleton))
# tuple packing t
= 12, 21, 'ab' #tuple in a for
print(t[0]) t = 1,2,3
for el in t:
# empty tuple (0 items) print el
empty = ()

# sequence unpacking # Tuples may be nested


x, y, z = t u = t, (23, 32)
print (x, y, z) print(u)
Dicionar
Un dicionar este o multime de perechi (cheie, valoare).
Cheile trebnuie sa fie imutabile.
Operations:
creare {} sau {'num': 1, 'denom': 2}
accesare valoare pe baza unei chei
adaugare/modificare pereche (cheie, valoare)
tergere pereche (cheie, valoare)
verificare dac cheia exist

#create a dictionary #set a value for a key


a = {'num': 1, 'denom': 2} a['num'] = 3
print(a) print(a)
print(a['num'])
#get a value for a key
print(a['num'])

#delete a key value pair #check for a key


del a['num'] if 'denom' in a:
print (a) print('denom = ', a['denom'])
if 'num' in a:
print('num = ', a['num'])
Variables

Variabl: Variabl in Python:


nume nume
valoare valoare
tip tip
domeniu domeniu
operaii operaii
locaie de memorie locaie de memorie

Introducerea unei variabile ntr-un program asignare

Expressi
O combinaie de valori, constante, variabile, operatori i funcii care sunt
interpretate conform regulilor de preceden, calculate i care produc o alt
valoare
Exemple:
numeric : 1 + 2
boolean: 1 < 2
string : 1 + 2

Funcii utile:
help(instructiune) - ajutor
id(x) identitatea obiectului
dir()
locals() / globals() - nume definite (variabile, funcii, module, etc)
Instruciuni

Operaiile de baz ale unui program. Un program este o secven de


instruciuni

Atribuire/Legare
Instructiunea =.
Atribuirea este folosit pentru a lega un nume de o variabil
Poate fi folosit i pentru a modifica un element dintr-o
secvena mutabil.
Legare de nume:
x = 1 #x is a variable (of type int)
Re-legare name:
x = x + 2 #a new value is assigned to
x
Modificare secven:
y = [1, 2] #mutable sequence
y[0] = -1 #the first item is bound to
-1

Blocuri
Parte a unui program care este executat ca o unitate
Secven de instruciuni
Se realizeaz prin identarea liniilor (toate instruciunile
identate la acelai nivel aparin aceluiai bloc
Instruciuni - If, While
def gcd(a, b):
"""
Return the greatest common divisor of two positive integers.
"""
if a == 0:
return b
if b == 0:
return a

while a != b:
if a > b:
a = a - b
else:
b = b - a
return a

print gcd(7,15)
Instruciuni For
#use a list literal
for i in [2,-6,"a",5]:
print (i)

#using a variable
x = [1,2,4,5]
for i in x:
print (i)

#using range
for i in range(10):
print (i)

for i in range(2,100,7):
print (i)

#using a string
s = "abcde"
for c in s:
print (c)
Cum se scriu programe

Roluri n ingineria software


Programator/Dezvoltator
Folosete calculatorul pentru a scrie/dezvolta aplicaii
Client (stakeholders):
Cel interesat/afectat de rezultatele unui proiect.
Utilizatori
Folosesc/ruleaz programul.

Un proces de dezvoltare software este o abordare sistematic pentru


construirea, instalarea, ntreinerea produselor software. Indic:
Paii care trebuie efectuai.
Ordinea lor

Folosim la fundamentele programrii: un proces de dezvoltare


incremental bazat pe funcionaliti (simple feature-driven
development process)
Enun (problem statement)
Enunul este o descriere scurt a problemei de rezolvat.

Calculator - Problem statement


Profesorul (client) are nevoie de un program care ajut elevii (users) sa
invee despre numere raionale.
Programul ar trebui sa permite elevilor s efectueze operaii
aritmetice cu numere raionale

Cerine (requirements)
Cerinele definesc n detaliu de ce este nevoie n program din perspectiva
clientului. Definete:
Ce dorete clientul
Ce trebuie inclus n sistemul informatic pentru a
satisface nevoile clientuli.
Reguli de elaborare a cerinelor:
Cerinele exprimate corect asigur dezvoltarea sistemului
conform ateptrilor clienilor. (Nu se rezolv probleme ce
nu s-au cerut)
Descriu lista de funcionaliti care trebuie oferite de sistem.
Funcionalitile trebuie s clarifice orice ambiguiti din enun.
Funcionalitate

O funcie a sistemului dorit de client


descrie datele rezultatele i partea sistemul care este afectat
este de dimensiuni mici, poate fi implementat ntr-un timp relativ scurt
se poate estima
exprimat n forma aciune rezultat obiect
Aciunea o funcie pe care aplicaia trebuie s o furnizeze
Rezultatul este obinut n urma execuiei funciei
Obiect o entitate n care aplicaia implementeaz funcia

Calculator List de Funcionaliti


F1. Adun un numr raional n calculator.
F2. Sterge calculator.
F3. Undo reface ultima operaie (utilizatorul poate repeta aceast
operaie).
Proces de dezvoltare incremental bazat pe funcionaliti
Se creaz lista de funcionalitai pe baza enunului
Se planific iteraiile (o interaie conine una/mai
multe funcionaliti)
Pentru fiecare funcionalitate din iteraie
Se face modelare scenarii de rulare
Se creaz o lista de tascuri (activiti)
Se implementeaz i testeaz
fiecare activitate

Iteraie: O perioad de timp n cadrul creia se realizeaz o versiune


stabil i executabil a unui produs, mpreun cu documentaia suport

La terminarea iteraiei avem un program funcional care face ceva util


clientului

Examplu: plan de iteraii


Iteratio Planned features
n
I1 F1. Adun un numr raional n calculator.
I2 F2. Sterge calculator.
I3 F3. Undo reface ultima operaie (utilizatorul poate repeta
aceast operaie).
Modelare - Iteration modeling
La fiecare nceput de iteraie trebuie analizat funcionalitatea care urmeaza a fi
implementat.
Acest proces trebuie sa sigure nelegerea funcionalitii si sa rezulte un set de pai
mai mici (work item/task), activitai care conduc la realizarea funcionalitii
Fiecare activitate se poate implementa/testa independent

Iteraia 1 - Adun un numr raional n calculator.

Pentru programe mai simple putem folosi scenarii de rulare (tabelar) pentru a
nelege problema i modul n care funcionalitatea se manifest n program. Un
scenariu descrie interaciunea ntre utilizator i aplicaie.

Scenariu pentru funcionalitatea de adaugare numar raional


Utilizator Program Descriere
a 0 Tiprete totalul curent
b 1/2 Adun un numr raional
c 1/2 Tiprete totalul curent
d 2/3 Adun un numr raional
e 5/6 Tiprete totalul curent
f 1/6 Adun un numr raional
g 1 Tiprete totalul curent
h -6/6 Adun un numr raional
i 0 Tiprete totalul curent
List de activiti
Recomandri:
Definii o activitate pentru fiecare operaie care nu este
implementata deja (de aplicaie sa de limbajul Python), ex. T1,
T2.
Definii o activitate pentru implementarea interaciunii
program- utilizator (User Interface), ex. T4.
Definii o activitate pentru a implementa operaiile
necesare pentru interaciune utilizator cu UI, ex. T3.
Determinai dependenele ntre activiti (ex. T4 --> T3 --> T2
-->T1, unde --> semnific faptul ca o activitate depinde de o
alt activitate).
Facei un mic plan de lucru (T1,T2,T3,T4)

T1 Determinare cel mai mare divizor comun (punctele g, I din scenariu)


T2 Sum dou numere raionale (c, e, g, i)
T3 Implementare calculator: init, add, and total
T4 Implementare interfa utilizator
Activitate 1. Determinare cel mai mare divizor comun
Cazuri de testare
Un test case conine un set de intrri i rezultatele ateptate pentru fiecare intrare.
Date: a, b Rezultate: gcd (a, b): c, unde c este cel mai mare divizor comun

23 1

24 2

64 2

02 2

20 2

24 9 3

-2 0 ValueError

0 -2 ValueError
Programare procedural
Paradigm de programare
stil fundamental de scriere a programelor, set de convenii ce dirijeaz
modul n care gndim programele.

Programare imperativ
Calcule descrise prin instruciuni care modific starea programului. Orientat
pe aciuni i efectele sale

Programare procedural
Programul este format din mai multe proceduri (funcii, subrutine)
Ce este o funcie
O funcie este un bloc de instruciuni de sine stttor care are:
un nume,
poate avea o list de parametrii (formali),
poate returna o valoare
are un corp format din instructiuni
are o documentaie (specificaie) care include:
o scurt descriere
tipul i descriere parametrilor
condiii impuse paramterilor de intrare (precondiii)
tipul i descrierea valorii returnate
condiii impuse rezultatului, condiii care
sunt satisfcute n urma executrii (post-
condiii).
Excepii ce pot s apar

def max(a, b):


"""
Compute the maximum of 2 numbers
a, b - numbers
Return a number - the maximum of two integers.
Raise TypeError if parameters are not integers.
"""
if a>b:
return a
return b
def isPrime(a):
"""
Verify if a number is prime
a an integer value (a>1)
return True if the number is prime, False otherwise
"""
Funcii
Toate funciile noastre trebuie s:
foloseasc nume sugestive (pentru numele funciei, numele variabilelor)
s ofer specificaii
s includ comentarii
s fie testat

O funcie ca i n exemplu de mai joi, este corect sintactic (funcioneaz n Python) dar
la laborator/examen nu consideram astfel de funcii:

def f(k):
l = 2
while l<k and k % l>0:
l=l+1
return l>=k

Varianta acceptat este:

def isPrime(nr):
"""
Verify if a number is prime
nr - integer number, nr>1
return True if nr is prime, False otherwise
"""
div = 2 #search for divider starting from 2
while div<nr and nr % div>0:
div=div+1
#if the first divider is the number itself than the number is prime
return div>=nr;
Definiia unei funcii n Python

Folosind instruciunea def se pot definii funcii n python.


Interpretorul executa instruciunea def, acesta are ca rezultat introducerea
numelui funciei (similar cu definirea de variabile)
Corpul funciei nu este executat, este doar asociat cu numele funciei

def max(a, b):


"""
Compute the maximum of 2 numbers
a, b - numbers
Return a number - the maximum of two integers.
Raise TypeError if parameters are not integers.
"""
if a>b:
return a
return b
Apel de funcii
Un bloc de instruciuni n Python este un set de instruciuni care este
executat ca o unitate. Blocurile sunt delimitate folosind identarea.
Corpul unei funcii este un bloc de instruciuni i este executat n momentul
n care funcia este apelat.

max(2,5)

La apelul unei funcii se creaz un nou cadru de execuie, care :


informaii administrative (pentru depanare)
determin unde i cum se continu execuia programului (dupa
ce execuia funciei se termin)
definiete dou spaii de nume: locals i globals care
afecteaz execuia funciei.
Spaii de nume (namespace)

este o mapare ntre nume (identificatori) i obiecte


are funcionaliti similare cu un dicionar (in general
este implementat folisind tipul dicionar)
sunt create automat de Python
un spaiu de nume poate fi referit de mai multe cadre de execuie

Adugarea unui nume n spaiu de nume: legare ex: x = 2


Modificarea unei mapri din spaiu de nume: re-legare

n Python avem mai multe spaiile de nume, ele sunt create n momente
diferite i au ciclu de viaa diferit.
General/implicit creat la pornirea interpretorului, conine
denumiri predefinite (built-in)
global creat la incrcarea unui modul, conine nume globale
globals() - putem inspecta spaiu de nume global
local creat la apelul unei funcii, conine nume locale funciei
locals() - putem inspecta spaiu de nume local
Transmiterea parametrilor
Parametru formal este un identificator pentru date de intrare. Fiecare apel
trebuie s ofere o valoare pentru parametru formal (pentru fiecare
parametru obligatoriu)
Parametru actual valoare oferit pentru parametrul formal la apelul
funciei.

Parametrii sunt transmii prin referin. Parametru formal


(identificatorul) este legat la valoarea (obiectul) parametrului
actual.
Parametrii sunt introdui n spaiu de nume local

def change_or_not_immutable(a):
print ('Locals ', locals())
print ('Before assignment: a = ', a, ' id = ', id(a))
a = 0
print ('After assignment: a = ', a, ' id = ', id(a))

g1 = 1 #global immutable int


print ('Globals ', globals())
print ('Before call: g1 = ', g1, ' id = ', id(g1))
change_or_not_immutable(g1)
print ('After call: g1 = ', g1, ' id = ', id(g1))

def change_or_not_mutable(a):
print ('Locals ', locals())
print ('Before assignment: a = ', a, ' id = ', id(a))
a[1] = 1
a = [0]
print ('After assignment: a = ', a, ' id = ', id(a))

g2 = [0, 1] #global mutable list


print ('Globals ', globals())
print ('Before call: g2 = ', g2, ' id = ', id(g2))
change_or_not_mutable(g2)
print ('After call: g2 = ', g2, ' id = ', id(g2))
Vizibilitatea variabilelor
Domeniul de vizibilitate (scope) Definete vizibilitatea unui nume ntr-un
bloc.
Variabilele definite ntr-o funcie au domenul de vizibilitate local
(funcia) se poate accesa doar n interiorul funciei
Variabilele definite ntr-un modul au vizibilitate global (global pe
modul)
Orice nume (variabile, funcii) poate fi folosit doar dupa ce a fost
legat (prima atribuire)
Parametrii formali au domeniu de vizibilitate funcia (aparin
spaiului de nume local)

global_var = 100

def f():
local_var = 300
print local_var
print global_var
Domeniu de vizibilitate
Reguli de accesare a variabilelor (sau orice nume) ntr-o funcie:
cand se folosete un nume de variabil ntr-o funcie se caut
n urmtoarele ordine n spaiile de nume:
spaiu local
spaiu local funciei exterioare (doar dac avem funcie declarat
n interorul altei funcii)
spaiu global (nume definite n modul)
spaiul built-in
operatorul = schimba/creaz variabile n spaiu de nume local
Putem folosi declaraia global pentru a referi/importa o
variabil din spaiu de nume global n cel local
nonlocal este folosit pentru a referi variabile din funcia
exterioar (doar dac avem funcii n funcii

a = 100 a = 100
def f(): def f():
a = 300 global a
print (a) a = 300
print (a)
f()
print (a) f()
print (a)

globals() locals() - funcii built-in prin care putem inspecta spaiile de nume

a = 300
def f():
a = 500
print (a) print
locals()
print globals()

f()
print (a)
Cum scriem funcii Cazuri de testare
nainte s implementm funcia scriem cazuri de testare pentru:
a specifica funcia (ce face, pre/post condiii, excepii)
ca o metod de a analiza problema
s ne punem n perspectiva celui care folosete funcia
pentru a avea o modalitate sa testam dup ce implementm

Un caz de testare specific datele de intrare i rezultatele care le ateptam


de la funcie

Cazurile de testare:
se pot face n format tabelar, tabel cu date/rezultate.
executabile: funcii de test folosind assert
biblioteci/module pentru testare automat

Instruciunea assert permite inserarea de aseriuni (expressi care ar


trebui sa fie adevrate) n scopul depanrii/verificrii aplicaiilor.

assert expresie

Folisim assert pentru a crea teste automate


Funcii de test - Calculator
1 Funcionalitate 1. Add a number to calculator.
2 Scenariu de rulare pentru adugare numr
3 Activiti (Workitems/Tasks)
T1 Calculeaz cel mai mare divizor comun
T2 Sum dou numere raionale
T3 Implementare calculator: init, add, and total
T4 Implementare interfa utilizator

T1 Calculeaz cel mai mare divizor comun


Cazuri de testare Format tabelar Funcie de test
Input: (params a,b) Output: gdc(a,b)
def test_gcd():
23 1 assert gcd(2, 3) == 1
24 2 assert gcd(2, 4) == 2
assert gcd(6, 4) == 2
64 2
assert gcd(0, 2) == 2
02 2 assert gcd(2, 0) == 2
20 2 assert gcd(24, 9) == 3

24 9 3

Implementare gdc
def gcd(a, b):
"""
Compute the greatest common divisor of two positive integers
a, b integers a,b >=0
Return the greatest common divisor of two positive integers.
"""
if a == 0:
return b
if b == 0:
return a
while a != b:
if a > b:
a = a - b
else:
b = b - a
return a
Cum se scriu funcii
Dezvoltare dirijat de teste (test-driven development - TDD)
Dezvoltarea drijat de teste presupune crearea de teste automate, chiar
nainte de implementare, care clarific cerinele
Paii TDD pentru crearea unei funcii:
Addaug un test
Scriei o funcie de test (test_f()) care conine
cazuri de testare sub forma de aseriuni
(instruciuni assert).
La acest pas ne concentram la specificaiile funciei
f.
Definim funcia f: nume, parametrii, precondiii,
post-condiii, i corpul gol (instruciunea pass).
Rulm toate testele i verificm ca noul test pic
Pe parcursul dezvoltrii o sa avem mai multe
funcii, astfel o s avem mai multe funcii de test .
La acest pas ne asigurm ca toate testele
anterioare merg, iar testul nou adugat pic.
Scriem corpul funciei
La acest pas avem deja specificaiile, ne
concentrm doar la implementarea funciei
conform specificaiilor i ne asigurm ca noile
cazuri de test scrise pentru funcie trec (funcia de
test)
La acest pas nu ne conentrm la
aspecte technice (cod duplicat,
optimizri, etc).
Rulm toate testele i ne asigurm c trec
Rulnd testele ne asigurm c nu am stricat nimic
i noua funcie este implementat conform
specificaiilor
Refactorizare cod
La acest pas inbuntim codul,
folosind refactorizri
Dezvoltare dirijat de teste (test-driven development - TDD)
Dezvoltarea drijat de teste presupune crearea de teste automate, chiar
nainte de implementare, care clarific cerinele

Paii TDD pentru crearea unei funcii:


Addaug un test creaz teste automate
Rulm toate testele i verificm ca noul test pic
Scriem corpul funciei
Rulm toate testele i ne asigurm c trec
Refactorizm codul
TDD Pas 1. Creare de teste automate
Cnd lucrm la un task ncepem prin crearea unei funcii de test

Task: Calculeaza cel mai mare divizor comun

def test_gcd():
"""
test function for gdc
"""
assert gcd(0, 2) == 2
assert gcd(2, 0) == 2
assert gcd(2, 3) == 1
assert gcd(2, 4) == 2
assert gcd(6, 4) == 2
assert gcd(24, 9) == 3

Ne concentrm la specificarea funciei.


def gcd(a, b):
"""
Return the greatest common divisor of two positive integers.
a,b integer numbers, a>=0; b>=0
return an integer number, the greatest common divisor of a and b
"""
pass
TDD Pas 2 - Rulm testele
#run the test - invoke the test function
test_gcd()

Traceback (most recent call last):


File "C:/curs/lect3/tdd.py", line 20, in <module> test_gcd()
File "C:/curs/lect3/tdd.py", line 13, in test_gcd
assert gcd(0, 2) == 2
AssertionError

Validm c avem un test funcional se execut, eueaz.

Astfel ne asigurm c testul este executat i nu avem un test


care trece far a implementa ceva testul ar fi inutil
TDD Pas 3 Implementare
implementare funcie conform specificaiilor (pre/post
condiii), scopul este sa trac testul
soluie simpl, fr a ne concentra pe optimizri,
evoluii ulterioare, cod duplicat, etc.

def gcd(a, b):


"""
Return the greatest common divisor of two positive integers.
a,b integer numbers, a>=0; b>=0
return an integer number, the greatest common divisor of a and b
"""
if a == 0:
return b
if b == 0:
return a
while a != b:
if a > b:
a = a - b
else:
b = b - a
return a
TDD Pas 4 Executare funcii de test-
toate cu succes
>>> test_gcd()
>>>

Dac toate testele au trecut codul este testat, e conform


specificaiilor i nu s-au introdus erori (au trecut i testele scrise
anterior)

TDD Pas 5 Refactorizare cod


restructurearea codului folosind refactorizri
Refactorizare
Restructurarea codului, alternd structura intern fr a modifica
comportamentul observabil.
Scopul este de a face codul mai uor de:
neles
ntreinut
extins

Semnale c este nevoie de refactorizare (code smell) elemente ce pot


indica probleme mai grave de proiectare:
Cod duplicat
Metode lungi
Liste lungi de paramtetrii
Instruciuni condiionale care determin diferene
de comportament
Refactorizare: Redenumire funcie/variabil
redenumim funcia/variabila alegnd un nume sugestiv
def verify(k): def isPrime(nr):
""" """
Verify if a number is prime Verify if a number is prime
nr - integer number, nr>1 nr - integer number, nr>1
return True if nr is prime return True if nr is prime
""" """
l = 2 div = 2 #search for divider
while l<k and k % l>0: while div<nr and nr % div>0:
l=l+1 div=div+1
return l>=k #if the first divider is the
# number itself than nr is prime
return div>=nr;
Refactorizare: Extragerea de metode
parte dintr-o funcie se transform ntr-o
funcie separat
o expresie se transform ntr-o funcie
.
def startUI(): def getUserCommand():
list=[] print """
(list) Print the application menu
#read user command return the selected menu
menu = """ """
Enter command: menu = """
1-add element Enter command:
0-exit 1-add
""" element 0-
print(menu) exit
cmd=input("") """
while cmd!=0: print(menu)
if cmd==1: cmd=input("")
nr=input("Give element:") return cmd
add(list, nr)
print list def startUI():
#read user command list=[]
menu = """ print list
Enter command: cmd=getUserCommand()
1-add element while cmd!=0:
0-exit if cmd==1:
""" nr=input("Give element:")
print(menu) add(list, nr)
cmd=input("") print list
cmd=getUserCommand()
startUI()
startUI()
Refactorizare: Substituire algoritm
def isPrime(nr): def isPrime(nr):
""" """
Verify if a number is prime Verify if a number is prime
nr - integer number, nr>1 nr - integer number, nr>1
return True if nr is prime return True if nr is prime
""" """
div = 2 #search for divider for div in range(2,nr):
while div<nr and nr % div>0: if nr%div == 0:
div=div+1 return False
#if the first divider is the return True
# number itself than nr is prime
return div>=nr;
Calculator versiune procedural
Modular programming

Descompunerea programului n module (componente separate


interschimbabile) avnd n vedere:
separarea conceptelor
coeziunea elementelor dintr-un modul
cuplarea ntre module
ntreinerea i reutilizarea codului
arhitectura stratificat

Modulul este o unitate structural separat, interschimbabil cu


posibilitatea de a comunica cu alte module.
O colecie de funcii i variabile care implementeaz o funcionalitate bine
definit
Modul n Python
Un modul n Python este un fiier ce conine instruciuni i definiii Python.
Modul
nume: Numele fiierului este numele modulului plus extensia .py
variabila name
este main dac modulul este executat de sine stttor
este numele modulului
docstring: Comentariu multiline de la nceputul modulului. Ofer o
descriere a modulului: ce conine, care este scopul, cum se foloseste, etc.
Variabila doc
instruciuni: definiii de funcii, variabile globale per modul, cod de
iniializare
Import de module
Modulul trebuie importat nainte de a putea folosi.
Instruciunea import:
Caut n namespace-ul global, dac deja exist modulul
nseamn ca a fost deja importat i nu mai e nevoie de alte
aciuni
Caut modulul i dac nu gasete se arunc o eroarea
ImportError
Dac modulul s-a gsit, se execut instruciunile din modul.

Instruciunile din modul (nclusiv definiiile de funcii) se execut doar o


singur dat (prima dat cnd modulul este importat n aplicaie).

from doted.package[module] import {module, function}}

from utils.numericlib import gcd

#invoke the gdc function from module utils.numericlib


print gdc(2,6)
from rational import *

#invoke the rational_add function from module rational


print rational_add(2,6,1,6)

import ui.console

#invoke the run method from the module ui.console


ui.console.run()
Calea unde se caut modulele (Module search path)
Instruciunea import caut fisierul modulname.py n:
directorul curent (directorul de unde s-a lansat aplicaia)
n lista de directoare specificat n variabila de mediu
PHYTONPATH
n lista de directoare specificat n variabila de mediu
PYTHONHOME (este calea de instalare Python; de exemplu
pe Unix, n general este .:/usr/local/lib/python.

Iniializare modul
Modulul poate conine orice instruciuni. Cnd modulul este importat
prima dat se execut toate instruciunile.Putem include instruciuni
(altele dect definiiile de funcii) care iniializeaz modulul.
Domeniu de vizibilitate n modul
La import:
se creaz un nou spaiu de nume
variabilele i funciile sunt introduse n noul spaiu de nume
doar numele modulului ( name ) este adugat n spaiul de
nume curent.

Putem folosi instruciunea built-in dir() dir(module_name) pentru a


examina coninutul modulului

#only import the name ui.console into the current symbol table
import ui.console

#invoke run by providing the doted notation ui.console of the


package
ui.console.run()

#import the function name gdc into the local symbol table
from utils.numericlib import gcd

#invoke the gdc function from module utils.numericlib


print gdc(2,6)

#import all the names (functions, variables) into the local


symbol table
from rational import *

#invoke the rational_add function from module rational


print rational_add(2,6,1,6)
Pachete n Python
Modalitate prin care putem structura modulele.
Dac avem mai multe module putem organiza ntr-o structur de directoare
Putem referi modulele prin notaia pachet.modul

Fiecare director care conine pachete trebuie sa conin un fiier init .py. Acesta
poate conine i instruciuni (codul de iniializare pentru pachet)
Eclipse + PyDev IDE (Integrated Development Environment)
Eclipse: Mediu de dezvoltare pentru python (printre altele).
Pydev: Plugin eclipse pentru dezvoltare aplicaii Python n
Eclipse

Permite crearea, rularea, testarea, depanarea de aplicaii python

Instalare:
Java JRE 7 (varianta curenta de PyDev funcioneaz doar cu aceast
versiune de Java
Eclipse Luna (sau alta versiune de eclipse de la 3.5 n sus)
Instalat pluginul de PyDev

Detalii pe: pydev.org


http://pydev.org/manual_101_install.html

Proiect

Editor Python

ProjectExplorer: Pachete/Module

Outline: Funcii

Rulare/Depanare programe
Cum organizm aplicaia pe module i pachete

Se creaz module separate pentru:


Interfa utilizator - Funcii legate de interaciunea cu
utilizatorul. Conine instruciuni de citire tiprire, este
singurul modul care conine tiparire-citire
Domeniu (Domain / Application) Conine funcii legate de
domeniul problemei
Infrastructur Funcii utilitare cu mare potenial de
refolosire (nu sunt strict legate de domeniul
problemei)
Coordonator aplicaie Iniializare/configurare i
pornire aplicaie
Calculator versiune modular
Organizarea aplicaiei pe funcii i module
Responsabiliti
Responsabilitate motiv pentru a schiba ceva
responsabilitate pentru o funcie: efectuarea unui calcul
responsabilitate modul: responsabilitile tuturo funciilor din modul

Principul unei singure responsabiliti - Single responsibility principle


(SRP)

O funcie/modul trebuie s aib o singur responsabilitate (un singur motiv de schimbare).

#Function with multiple responsibilities


#implement user interaction (read/print)
#implement a computation (filter)
def filterScore():
st = input("Start score:")
end = input("End score:")
for c in l:
if c[1]>st and c[1]<end:
print c

Multiple responsibiliti conduc la


Dificulti n nelegere i utilizare
Imposibilitatea de a testa
Imposibilitatea de a refolosi
Dificulti la ntreinere i evoluie
Separation of concerns

Principiu separrii responsabilitilor - Separation of concerns (SoC)


procesul de separare a unui program n responsabiliti care nu se suprapun

def filterScoreUI(): def testScore():


st = input("Start sc:") l = [["Ana", 100]]
end = input("End sc:") assert filterScore(l,10,30)==[]
rez = filtrareScore(l,st, end) assert filterScore(l,1,30)==l
for e in rez: l = [["Ana", 100],["Ion", 40],["P", 60]]
print e assert filterScore(l,3,50)==[["Ion", 40]]

def filterScore(l,st, end):


"""
filter participants
l - list of participants
st, end - integers -scores
return list of
participants
filtered by st end score
"""
rez = []
for p in l:
if p[1]>st and p[1]<end:
rez.append(p)

Dependene
funcia: apeleaz o alt funcie
modul: orice funcie din modul apelaz o funcie din alt modul

Pentru a uura ntreinerea aplicaiei este nevoie de gestiunea

dependenelor
Cuplare

Msoar intensitatea legturilor dintre module/funcii

Cu ct exist mai multe conexiuni ntre module cu att modulul este mai greu de neles,
ntreinut, refolosit i devine dificil izolarea prolemelor cu ct gradul de cuplare este mai
sczut cu att mai bine

Gradul de cuplare sczut(Low coupling) faciliteaz dezvoltarea de aplicai care pot fi uor
modificate (interdependena ntre module/funcii este minim astfel o modificare afecteaz doar
o parte bine izolat din aplicaie)
Coeziunea
Msoar ct de relaionate sunt resposabilitile unui element din program (pachet,modul,
clas)
Modulul poate avea:
Grad de coeziune ridicat (High Cohesion): elementele implementeaz
responsabiliti nrudite
Grad de coeziune sczut (Low Cohesion): implementeaz responsabiliti
diverse din arii diferite (fr o legatur conceptual ntre ele)

Un modul puternic coeziv ar trebui s realizeze o singur sarcin i sa necesite interaciuni


minime cu alte pri ale programului.

Dac elementele modulului implementeaza responsabiliti disparate cu att modulul este mai
greu de Modulele ar trebui sa aib grad de coeziune ridicat
neles/ntreinut
Arhitectur stratificat (Layered Architecture)

Structurarea applicaiei trebuie s aib n vedere :


Minimizarea cuplrii ntre module (modulele nu trebuie sa cunoasc detalii despre alte
module, astfel schimbrile ulteroare sunt mai uor de implementat)
Maximizare coeziune pentru module (coninutul unui modul izoleaza un concept bine
definit)

Arhitectur stratificat este un ablon arhitectural care permite dezvoltarea de sisteme flexibile
n care componentele au un grad ridicat de independen

Fiecare strat comunic doar cu startul imediat urmtor (depinde doar doar de
stratul imediat urmtor)
Fiecare strat are o interfa bine definit (se ascun detaliile), interfa folosit de stratul
imediat superior
Arhitectur stratificat

Nivel prezentare (User interface / Presentation )


implementeaz interfaa utilizator (funcii/module/clase)

Nivel logic (Domain / Application Logic)


ofer funcii determinate de cazurile de utilizare
implementeaza concepte din domeniul aplicaiei
Infrastructur
funcii/module/clase generale, utilitare

Coordonatorul aplicaiei (Application coordinator)


asambleaz i pornete aplicaia
Layered Architecture simple example
#Ui
def filterScoreUI(): #manage the user interaction
st = input("Start sc:")
end = input("End sc:")
rez = filterScoreDomain(st, end)
for e in rez:
print (e)

#domain
l = [["Ion",50],["Ana",30],["Pop",100]]
def filterScoreDomain(st, end): #filter the score board
global l
rez = filterMatrix(l, 1, st, end)
l = rez
return rez

#Utility function - infrastructure


def filterMatrix(matrice, col, st, end): #filter matrix lines
linii = []
for linie in matrice:
if linie[col]>st and linie[col]<end:
linii.append(linie)
return linii
Organizarea proiectelor pe pachete/module
Erori i excepii
Erori de sintax erori ce apar la parsarea codului
while True print("Ceva"):
pass
File "d:\wsp\hhh\aa.py", line 1
while True print("Ceva"):
^
SyntaxError: invalid syntax

Codul nu e corect sintactic (nu respect regulile limbajului)


Excepii
Erori detectate n timpul rulrii.

Excepiile sunt aruncate n momentul n care o eroare este detectat:


pot fi aruncate de interpretorul python
aruncate de funcii pentru a semnala o situaie exceptional, o eroare
ex. Nu sunt satisfcute precondiiile

>>> x=0
>>> print 10/x

Trace back (most recent call last):


File "<pyshell#1>", line 1, in <module>
print 10/x
ZeroDivisionError: integer division or modulo by zero
def rational_add(a1, a2, b1, b2):
"""
Return the sum of two rational numbers.
a1,a2,b1,b2 integer numbers, a2<>0 and b2<>0
return a list with 2 int, representing a rational number a1/b2 + b1/b2
Raise ValueError if the denominators are zero.
"""
if a2 == 0 or b2 == 0:
raise ValueError("0 denominator not allowed")
c = [a1 * b2 + a2 * b1, a2 * b2]
d = gcd(c[0], c[1])
c[0] = c[0] / d
c[1] = c[1] / d
return c
Modelul de execuie (Execution flow)

Excepiile intrerup execuia normal a instruciunilor


Este un mechanism prin care putem ntrerupe execuia normal a unui bloc de instruciuni
Programul contiun execuia n punctul n care excepia este tratat (rezolvat) sau ntrerupe de
tot programul

def compute(a,b):
print ("compute :start ")
aux = a/b
print ("compute:after division")
rez = aux*10
print ("compute: return")
return rez

def main():
print ("main:start")
a = 40
b = 1
c = compute(a, b)
print ("main:after compute")
print ("result:",c*c)
print ("main:finish")

main()
Tratarea excepiilor (Exception handling)
Procesul sistematic prin care excepiile aprute n program sunt gestionate, executn
aciuni necesare pentru remedierea situaiei.

try:
#code that may raise exceptions
pass
except ValueError:
#code that handle the error
pass

Excepiile pot fi tratate n blocul de instruciuni unde apar sau n orice bloc exterior care
in mod direct sau indirect a apelat blocul n care a aprut excepia (excepia a fost
aruncat)
Dac excepia este tratat, acesta oprete rularea programului

raise, try-except statements


try:
calc_add (int(m), int(n))
printCurrent()
except ValueError:
print ("Enter integers for m, n, with n!=0")
Tratarea selectiv a excepiilor
avem mai multe clauze except,
este posibil sa propagm informaii despre excepie
clauza finally se execut n orice condiii (a aprut/nu a aprut excepia)
putem arunca excepii proprii folosind raise

def f():
# x = 1/0
raise ValueError("Error Message") # aruncm excepie

try:
f()
except ValueError as msg:
print "handle value error:", msg
except KeyError:
print "handle key error"
except:
print "handle any other errors"
finally:
print ("Clean-up code here")

Folosii excepii doar pentru:


A semnala o eroare semnalam situaia n care funcia nu poate respecta
post condiia, nu poate furniza rezultatul promis n specificaii
Putem folosi pentru a semnala nclcarea

precondiiilor Nu folosii excepii cu singurul scop de a altera fluxul de

execuie
Specificaii
Nume sugestiv
scurta descriere (ce face funcia)
tipul i descrierea parametrilor
condiii asupra parametrilor de intrare (precondiii)
tipul, descrierea rezultatului
relaia ntre date i rezultate (postcondiii)
Excepii care pot fi aruncate de funcie, i condiiile in care se arunc

def gcd(a, b):


"""
Return the greatest common divisor of two positive integers.
a,b integer numbers
return an integer number, the greatest common divisor of a and b
Raise ValueError if a<=0 or b<=0
"""
Review calculator modular
Cateva probleme:
Starea calculatorului:
varianta cu variabil global:
avem mai multe variabile globale care pot fi cu uurint accesate din exterior
(posibil stricand starea calculatorului)
variabila global face testarea mai dificil
nu este o legtur clar ntre aceste variabile (starea calculatorului este
mprtiat n cod)
varianta far variabile globale:
starea calculatorului este expus (nu exist garanii ca metodele se apeleaza cu
un obiect care reprezint calculatorul)
trebuie sa transmitem starea, ca parametru pentru fiecare funcie legat de
calculator
Numere raionale
reprezentarea numerelor este expusa: ex: rez= suma(total[0],total[1],a,b) ,
putem cu uurina altera numrul raional (ex. Facem total[0] = 8 care posibil
duce la nclcarea reguli cmmdc(a,b) ==1 pentru orice numrul raional a/b
codul pentru adunare, nmultire, etc de numere rationale este diferit de modul in care
facem operaii cu numere intregi. Ar fi de preferat sa putem scrie r = r1+r2 unde
r,r1,r2 sunt numere rationale
Programare orientat obiect
Este metod de proiectare i dezvoltare a programelor:
Ofer o abstractizare puternic i flexibil
Programatorul poate exprima soluia n mod mai natural (se
concentreaz pe structura soluiei nu pe structura calculatorului)
Descompune programul ntr-un set de obiecte, obiectele sunt
elementele de baz
Obiectele interacioneaza pentru a rezolva problema, exist relaii
ntre clase
Clasele introduc tipuri noi de date, modeleaza elemente din spaiul
problemei, fiecare obiect este o instana a unui tip de data (clas)
Clas
Defineste in mod abstract caracteristicile unui lucru.
Descrie dou tipuri de atribute:
cmpuri (proprietati) descriu caracteristicile
metode (operaii) descriu comportamentul

Clasele se folosesc pentru crearea de noi tipuri de date (tipuri de date definite de utilizator)
Tip de date:
domeniu
operatii

Clasele sunt folosite ca un sablon pentru crearea de obiecte (instante), clasa defineste
elementele ce definesc starea si comportamentul obiectelor.
Definitie de clas n python

class MyClass:
<statement 1>
.
<statement n>

Este o instruciune executabil, introduce un nou tip de date cu numele specificat.

Instruciunile din interiorul clasei sunt n general definiii de funcii, dar i alte instruciuni
sunt permise

Clasa are un spaiu de nume propriu, definiiile de funcii din interiorul clasei (metode)
introduc numele funciilor n acest spaiu de nume nou creat. Similar i pentru variabile
Obiect
Object (instan) este o colecie de date i funcii care opereaz cu aceste date

Fiecare obiect are un tip, este de tipul clasei asociate: este instaa unei clase

Obiectul:
inglobeaz o stare: valorile campurilor
folosind metodele:
putem modifica starea
putem opera cu valorile ce descriu starea obiectelor

Fiecare obiect are propiul spaiu de nume care conine campurile i metodele.
Creare de obiecte. Creare de instane a unei clase ( init )
Instanierea une clase rezulte in obiecte noi (instane). Pentru creara de obiecte se
foloseste notaie similar ca i la funcii.

x = MyClass()

Operaia de instaniere (apelul unei clase) creaz un obiect nou, obiectul are tipul
MyClass

O clas poate defini metoda special init care este apelat n momentul instanierii

class MyClass:
def init (self):
self.someData = []

init :
creaz o instan
foloseste self pentru a referi instana (obiectul) curent (similar cu this din alte
limbaje orientate obiect)

Putem avea metoda init care are i ali parametrii n afar de self
Campuri

x = RationalNumber(1,3) y
= RationalNumber(2,3)
x.m = 7
x.n = 8
y.m = 44
y.n = 21

class RationalNumber:
"""
Abstract data type for rational numbers
Domain: {a/b where a and b are integer numbers b!=0}
"""

def init (self, a, b):


"""
Creates a new instance of RationalNumber
"""
#create a field in the rational number
#every instance (self) will have this field
self.n = a
self.m = b

self.n = a vs n=a

1 Creaz un atribut pentru instana curent


2 Creaz o variabil local funciei
Metode
Metodele sunt funci definite in interiorul clasei care au acces la valorile campurilor unei
instane.

n Python metodele au un prim argument: instana curent


Toate metodele primesc ca prim parametru obiectul curent (self)

def testCreate():
"""
Test function for creating rational numbers
"""
r1 = RationalNumber(1,3) #create the rational number 1/3
assert r1.getNominator()==1
assert r1.getDenominator()==3
r1 = RationalNumber(4,3) #create the rational number 4/3
assert r1.getNominator()==4
assert r1.getDenominator()==3

class RationalNumber:
"""
Abstract data type rational numbers
Domain: {a/b where a,b integer numbers, b!=0, greatest common divisor
a, b =1}
"""
def init (self, a, b):
"""
Initialize a rational number
a,b integer numbers
"""
self. nr = [a, b]

def getDenominator(self):
"""
Getter method
return the denominator of the rational number
"""
return self. nr[1]

def getNominator(self):
""""
Getter method
return the nominator of the method
"""
return self. nr[0]
Metode speciale. Suprncrcarea operatorilor. (Operator overloading)
str - conversie in tipul string (print representation)
def str (self):
"""
provide a string representation for the rational number
return a string
"""
return str(self. nr[0])+"/"+str(self. nr[1])

def testCompareOperator(): def lt (self, ot):


""" """
Test function for < Compare 2 rational numbers (less than)
> """ self the current instance
r1 = RationalNumber(1, 3) ot a rational number
r2 = RationalNumber(2, 3) return True if self<ot,False otherwise
assert r2>r1 """
assert r1<r2 if self.getFloat()<ot.getFloat():
return True
return False

lt , le , gt , ge - comparaii (<,<=,>,>=)

def testEqual(): def eq (self, other):


""" """
test function for Verify if 2 rational are
== """ equals other - a rational
r1 = RationalNumber(1, 3) number return True if the
assert r1==r1 instance is
r2 = RationalNumber(1, 3) equal with other
assert r1==r2 """
r1 = RationalNumber(1, 3) return self. nr==other. nr
r1 = r1.add(RationalNumber(2, 3))
r2 = RationalNumber(1, 1)
assert r1==r2

eq - verify if equals
Operator overloading

add (self, other) - pentru a folosi operatorul +


def testAddOperator(): def add (self,other):
""" """
Test function for the + operator Overload + operator
""" other - rational
r1 = RationalNumber(1,3) number return a rational
r2 = RationalNumber(1,3) number,
r3 = r1+r2 the sum of self and other
assert r3 == RationalNumber(2,3) """
return self.add(other)

Metoda mul (self, other) - pentru operatorul *

Metoda setItem (self,index, value) dac dorim ca obiectele noastre sa se comporte


similar cu liste/dicionare, sa putem folosi []
a = A()
a[index] = value

getItem (self, index) sa putem foloi obiecul ca si o secven


a = A()
for el in a:
pass

len (self) - pentru len

getslice (self,low,high) - pentru operatorul de slicing


a = A()
b = a[1:4]

call (self, arg) - to make a class behave like a function, use the ()
a = A()
a()
Vizibilitate i spaii de nume n Python

Spaiu de nume (namespace) este o mapare intre nume i


obiecte Namespace este implementat n Python folosind
dictionarul Cheie: Nume
Valoare Object

Clasa introduce un nou spaiu de nume


Metodele, campurile sunt nt-un spaiu de nume sparat, spaiu de nume corespunztor
clase.

Toate regulile (legare de nume, vizibilitate/scope, paramterii formali/actuali, etc.) legate de


denumiri(funciion, variable) sunt acelasi pentru attributele clasei (methode, campuri) ca si
pentru orice alt nume in python, doar trebuie luat n considerare ca avem un namespace dedicat
clasei
Atribute de clas vs atribute de instane

Variabile membre (cmpuri)


atribute de instae valorile sunt unice pentru fiecare instan (obiect)
atribute de clas valoarea este partajata de toate instanele clasei (toate obiectele de
acelai tip)

class RationalNumber:
"""
Abstract data type for rational numbers
Domain: {a/b where a and b are integer numbers b!=0}
"""
#class field, will be shared by all the instances
numberOfInstances =0

def init (self, a, b):


"""
Creates a new instance of RationalNumber
"""
self.n = a
self.m = b
RationalNumber.numberOfInstances+=1 # accessing class fields

numberOfInstances =0 def
init (self,n,m):
self.n = n
self.m = m
RationalNumber.numberOfInstances+=1

def testNumberInstances():
assert RationalNumber.numberOfInstances == 0
r1 = RationalNumber(1,3)
#show the class field numberOfInstances
assert r1.numberOfInstances==1
# set numberOfInstances from the class
r1.numberOfInstances = 8
assert r1.numberOfInstances==8 #access to the instance field
assert RationalNumber.numberOfInstances==1 #access to the class field

testNumberInstances()
Class Methods
Funcii din clas care nu opereaza cu o instana.
Alte limbaje: metode statice

class RationalNumber:
#class field, will be shared by all the instances
numberOfInstances =0

def init (self,n,m):


"""
Initialize the rational number
n,m - integer numbers
"""
self.n = n
self.m = m
RationalNumber.numberOfInstances+=1

@staticmethod
def getTotalNumberOfInstances():
"""
Get the number of instances created in the app
"""
return RationalNumber.numberOfInstances

def testNumberOfInstances():
"""
test function for getTotalNumberOfInstances
"""
assert RationalNumber.getTotalNumberOfInstances()==0
r1 = RationalNumber(2, 3)
assert RationalNumber.getTotalNumberOfInstances()==1

testNumberOfInstances()

ClassName.attributeName folosit pentru a accesa un atribut asociat clasei


(camp,metoda)
Decoratorul @staticmethod este folosit pentru a marca o funcie static. Aceste funcii
nu au ca prim argument (self) obiectul curent.
Principii pentru crearea de noi tipuri de date
ncapsulare

Datele care reprezint starea i metodele care manipuleaza datele sunt strns legate, ele
formeaz o unitate coeziv.
Starea si comportamentul ar trebui ncapsulat n acelasi unitate de program (clasa)

Ascunderea informaiilor
Reprezentarea interna a obiectelor (a strii) trebuie protejat fa de restul aplicaiei.
Ascunderea reprezentrii protejeaz integritatea datelor i nu permite modificarea starii din
exteriorul clasei, astfel se evit setarea, accidentala sau voita, unei stari inconsistente.

Clasa comunica cu exteriorul doar prin interfaa public (mulimea tuturor metodelor vizibile in
exterior) i ascunde orice detalii de implementare (modul n care am reprezentat datele,
algoritmii folosii,etc).

De ce:
Definirea unei interfee clare i ascunderea detaliilor de implementare asigur ca alte module
din aplicaie sa nu pot face modificri care ar duce la stari inconsistente. Permite evoluia
ulterioar (schimbare reprezentare, algoritmi etc) fr s afectm restul aplicaiei

Limitai interfaa (metodele vizibile n exterior) astfel nct s existe o libertate n modificarea
implementrii (modificare fr a afecta codul client)

Codul client trebuie s depind doar de interfaa clasei, nu de detalii de implementare. Dac
folosii acest principiu, atunci se pot face modificri fr a afecta restul aplicaiei
Membri publici. Membrii privai Ascunderea implementrii in Python

Trebuie sa protejm (ascundem) reprezentarea intern a clasei (implementarea)

In Python ascunderea implementrii se bazeaza pe convenii de nume.


_name sau name pentru un atribut semnaleaza faptul ca atributul este privat

Un nume care incepe cu _ sau semnaleaza faptul ca atributul (camp, metode) ar trebui tratat
ca fiind un element care nu face parte din interfaa public. Face parte din reprezentarea
intern a clasei, nu ar trebui accesat din exterior.
Recomandri
Creai metode pentru a accesa campurile clasei (getter)
folositi conveniile de nume _, pentru a delimita interfaa public a clasei de
detaliile de implementare
Codul client ar trebui sa funcioneze (fr modificri) chiar daca schimbam
reprezentarea intern, atta timp ct interfaa public rmne
neschimbat. Clasa este o abstractizare, o cutie neagra (black box)
Specificaiile funciilor trebuie sa fie independente de reprezentare
Cum crem clase
Folosim Dezvoltare dirijat de teste

Specificaiile (doumentaia) pentru clase includ:


scurt descriere
domeniul ce fel de obiecte se pot crea. n general descrie campurile clasei
Constrngeri ce se aplic asupra datelor membre: Ex. Invariants condiii care
sunt adevrate pentru ntreg ciclu de viaa al obiectului
class RationalNumber:
"""
Abstract data type rational numbers
Domain:{a/b where a,b integer numbers, b!=0, greatest common divisor a, b =1}
Invariant:b!=0, greatest common divisor a, b =1
"""
def init (self, a, b):

Se creaza funcii de test pentru:


Crearea de instane
Fiecare metod din clas

Campurilie clasei (reprezentarea) se declar private ( nume). Se creaz metode getter pentru
a accesa cmpurile clasei
Tipuri abstracte de date (Abstract data types)
Tip abstract de date:
operaiile sunt specificate independent de felul n care operaia este
implementat
operaiile sunt specificate independent de modul de reprezentare a datelor

Un tip abstract de date este: Tip de date+ Abstractizarea datelor + ncapsulare

Review Calculator rational varianta orientat obiect

Putem schimba cu usurina reprezentarea intern pentru clasa RationalNumber (folosim


a,b n loc de lista [a,b])
Diagrame UML

Unified Modeling Language (UML) - este un limbaj standardizat de


modelare destinat vizualizrii, specificrii, modelrii i documentrii
aplicaiilor.

UML include un set de notaii grafice pentru a crea modele vizuale ce


descriu sistemul.
Diagrame de clase
Diagrama UML de clase (UML Class diagrams) descrie structura
sistemului prezentnd clasele,atributele i relaiile intre aceste clase

Clasele sunt reprezentate prin dreptunghiuri ce conin trei zone:


Partea de sus numele clasei
Partea din mijloc cmpurile/atributele clasei
Partea de jos metodele/operaiile

Relaii UML
O relaie UML este un termen general care descrie o legatur logic ntre
dou elemente de pe o diagram de clase.
Un Link este relaia ntre obiectele de pe diagram. Este
reprezentat printr-o linie care conecteaza dou sau mai multe
dreptunghiuri.

Associeri
Asocierile binare se reprezint printr-o linie ntre dou clase.

O asociere poate avea nume, capetele asocieri pot fi adnotate cu nume de


roluri, multiplicitate, vizibilitate i alte proprieti. Asocierea poate fi uni-
direcional sau bi-direcional

Agregare
Agregarea este o asociere specializat. Este o asociere ce reprezint
relaia de parte-ntreg (part-whole) sau apartenena (part-of).

class Car: class Engine:


def init (self,eng,col): def init (self,cap,type):
""" """
Initialize a car initialize the engine
eng - engine cap positive integer
col - string, ie White type string
""" """
self. engine = eng self. capacity = cap
self. color = col self. type = type
def getColor(self):
""" def getCapacity(self):
Getter method for color """
return string Getter method for the capacity
""" """
return self. color return self. capacity
def getEngine(self): def getType(self):
""" """
Getter method for engine Getter method for type
return engine return string
""" """
return self. engine return self. type

Dependene, Pachete
Relaia de dependen este o asociere n care un element depinde sau
foloseste un alte element
Exemple de dependene:
creaz instane
are un parametru
foloseste un obiect n interiorul unei metode
Principii de proiectare
Creaz aplicaii care:
Sunt uor de neles, modificat, ntreinut, testat
Clasele abstracte, ncapsulare, ascunderea reprezentrii, uor de
testat, uor de folosit i refolosit

Scop general: gestiunea dependenelor


Single responsibility
Separation of concerns
Low Coupling
High Cohesion

Enun (Problem statement)


Scriei un program care gestiuneaz studeni de la o facultate (operaii
CRUD Create Read Update Delete)

Funcionaliti (Features)
F1 Adauga student
F2 vizualizare studeni
F3 caut student
F4 terge student

Plan de iteraii
IT1 - F1; IT2 F2; IT3 F3; IT4 - F4

Scenariu de rulare (Running scenario)


user app description

a add a student

give student id

give name

Ion

new student added

a add student

give student id

give name

id already exists, name can


not be empty

Arhitectur stratificat (Layered architecture)


Layer (strat) este un mecanism de structurare logic a elementelor ce
compun un sistem software
ntr-o arhitectur multi-strat, straturile sunt folosite pentru a aloca
responsabiliti n aplicaie.
Layer este un grup de clase (sau module) care au acelai set de
dependene cu alte module i se pot refolosi n circumstane similare.

User Interface Layer (View Layer, UI layer sau Presentation


layer)
Application Layer (Service Layer sau GRASP Controller
Layer)
Domain layer (Business Layer, Business logic Layer sau
Model Layer)
Infrastructure Layer (acces la date modaliti de
persisten, logging, network I/O ex. Trimitere de email,
sau alte servicii technice)

abloane Grasp
General Responsibility Assignment Software Patterns (or Principles)
conin recomandri pentru alocarea responsabilitilor pentru clase obiecte
ntr-o aplicaie orientat obiect.

High Cohesion
Low Coupling
Information Expert
Controller
Protected Variations
Creator
Pure Fabrication

High Cohesion
Aloc responsabilitile astfel nct coeziunea n sistem rmne
ridicat

High Cohesion este un principiu care se aplic pe pacursul dezvoltrii n


ncercarea de a menine elementele n sistem:
responsabile de un set de activiti nrudite
de dimensiuni gestionabile
uor de neles

Coeziune ridicat (High cohesion) nseamna ca responsabilitile pentru un


element din sistem sunt nrudite, concentrate n jurul aceluiai concept.

nprirea programelor n clase i starturi este un exemplu de activitate care


asigur coeziune ridicat n sistem.
Alternativ, coeziune slab (low cohesion) este situaia n care elementele
au prea multe responsabiliti, din arii diferite. Elementele cu coeziune
slab sunt mai greu de neles, reutilizat , ntreinut i sunt o piedic pentru
modificrile necesare pe parcursul dezvoltrii unui sistem

Low Coupling
Aloc responsabiliti astfel nct cuplarea rmne slab (redus)

Low Coupling ncurajeaz alocarea de responsabilitai astfel nct avem:


dependene puine ntre clase;
inpact sczut n sistem la schimbarea unei clase;
potenial ridicat de refolosire;

Forme de cuplare:
TypeX are un cmp care este de TypeY.
TypeX are o medod care refer o instan de tipul TypeY n orce
form (parameterii, variabile locale, valoare returnat, apel la
metode)
TypeX ete derivat direct sau indirect din clasa TypeY.

Information Expert
Aloc responsabilitatea clasei care are toate informaiile necesare
pentru a ndeplini sarcina

Information Expert este un principiu care ajut s determinm care este


clasa potrivit care ar trebui s primeasc responsabilitatea (o metod
noua, un cmp, un calcul).

Folosind principiu Information Expert ncercm sa determinm care sunt


informaiile necesare pentru a realiza ce se cere, determinm locul n care
sunt aceste informaii i alocm responsabilitatea la clasa care conine
informaiile necesare.

Information Expert conduce la alocarea responsabilitii n clasa care


conine informaia necesar pentru implementare. Ajut s rspundem la
ntrebarea Unde se pune metoda, cmpul
Information Expert

Point of Sale

application Cine este responsabil cu calcului


totalului?

Avem nevoie de toate SaleItems


pentru a calcula totalul.

Information Expert Sale

Conform Expert

SaleItem ar trebui sa fie responsabil


cu calculul subtotalului (quantity *
price)

1. Menine ncapsularea
2. Promoveaz cuplare slab
3. Promoveaz clase puternic coezive
4. Poate sa conduc la clase complexe - dezavantaj

Creator
Crearea de obiecte este o activitate important ntr-un sistem orientat
obiect. Care este clasa responsabil cu crearea de obiecte este o
proprietate fundabental care definete relaia ntre obiecte de diferite
tipuri.

ablonul Creator descrie modul n care alocm responsabilitatea de a crea


obiecte n sistem

n general o clasa B ar trebui s aib responsibilitatea de a crea obiecte de


tip A dac unul sau mai multe (de preferat) sunt adevrate:
Instana de tip B conine sau agreg instane de tip A
Instana de tip B gestioneaz instane de tip A
Instana de tip B folosete extensiv instane de tip A
Instana de tip B are informaiile necesare pentru a
iniializa instana A.

Work items

Task
T1 Create Student
T2 Validate student
T3 Store student (Create repository)
T4 Add student (Create Controller)
T5 Create UI

Task: create Student


def testCreateStudent(): class Student:
""" def init (self, id, name, adr):
Testing student creation """
""" Create a new student
st = Student("1", "Ion", "Adr") id, name, address
assert st.getId() == "1" String
assert st.getName() == "Ion" """
assert st.getAdr() == "Adr" self.id = id
self.name = name
self.adr = adr

def getId(self):
return self.id

def getName(self):
return self.name

def getAdr(self):
return self.adr

Protected Variations
Cum alocm responsabilitatea astfel nct variaiile curente i viitoare nu
vor afecta sistemul (nu va fi necesar o revizuire a sistemului, nu trebuie sa
facem schimbri majore n sistem)?

Protected variations: Crem o nou clas care ncapsuleaz aceste variaii.

ablonul Protected Variations protejeaz elementele sistemului de


variaiile/modificrile altor elemente din sistem (clase, obiecte, subsisteme)
ncapsulnd partea instabil ntr-o clas separat (cu o interfa public
bine delimitat care ulterior, folosind polimorfism, poate introduce variaii
prin noi implementri).

Task: Validate student


Design posibil pentru validare:

Algoritmul de validare:
Poate fi o metoda in clasa student
o metoda statica, o funcie
ncapsulat ntr-o clas
separat Poate semnala eroarea
prin:
returnare true/false
returnare lista de erori
excepii care conin lista de erori

Clas Validator : aplic Principiu Protect Variation

Pure Fabrication
Cnd un element din sistem ncalc primcipiul coeziunii ridicate i cuplare
slab (n general din cauza aplicrii succesive a ablonului expert):
Aloc un set de responsabiliti la o clas artificial (clas ce nu reprezint
ceva n domeniul problemei) pentru a oferi coeziune ridicat, cuplare
slab i reutilizare

Pure Fabrication este o clas ce nu reprezint un concept din domeniul


problemei este o clas introdus special pentru a menine cuplare slab i
coeziune ridicat n sistem.
Problema: Stocare Student (in memorie, fiier sau baz de date)
Expert pattern Clasa Student este expert, are toate informaiile, pentru
a realiza aceast operaie

Pure Fabrication - Repository


Problema: Stocare Student (in memorie, fiier sau baz de date)
Expert pattern Clasa Student este expert, are toate informaiile, pentru
a realiza aceast operaie
Dac punem responsabilitatea persistenei in clasa Student, rezult o clas
slab coeziva, cu potenial limitat de refolosire

Soluie Pure Fabrication

Clas creat cu responsabilitatea de a


salva/persista obiecte Student

Clasa student se poate reutiliza cu uurin


are High cohesion, Low coupling

Clasa StudentRepository este responsabil cu


problema gestiunii unei liste de studeni (s
ofere un depozit - persistent pentru obiecte
de tip student)

ablonul Repository
Un repository reprezint toate obiectele de un anumit tip ca si o mulime
de obiecte.
Obiecte sunt adugate, terse, modificate iar codul din repository
insereaza, sterge obiectele dintr-un depozit de date persistent.

Task: Create repository


def testStoreStudent():
st = Student("1", "Ion", "Adr")
rep = InMemoryRepository()
assert rep.size()==0
rep.store(st)
assert rep.size()==1
st2 = Student("2", "Vasile", "Adr2")
rep.store(st2)
assert rep.size()==2
st3 = Student("2", "Ana", "Adr3")
try:
rep.store(st3)
assert False
except ValueError:
pass

class InMemoryRepository:
"""
Manage the store/retrieval of students
"""
def init (self):
self.students = {}

def store(self, st):


"""
Store students
st is a student
raise RepositoryException if we have a student with the same id
"""
if st.getId() in self.students:
raise ValueError("A student with this id already exist")

if (self.validator!=None):
self.validator.validate(st)

self.students[st.getId()] = st

GRASP Controller
Scop: decuplarea sursei de evenimente de obiectul care gestioneaz
evenimentul. Decuplarea startului de prezentare de restul aplicaiei.

Controller este definit ca primul obiect dup stratul de interfa utilizator.


Interfaa utilizator folosete un obiect controller, acest obiect este
responsabil de efectuarea operaiilor cerute de utilizator.
Controller coordoneaz (controleaz) operaiile necesare pentru a realiza
aciunea declanat de utilizator.
Controlerul n general folosete alte obiecte pentru a realiza operaia, doar
coordoneaz activitatea.

Controllerul poate ncapsula informaii despre starea curent a unui use-


case. Are metode care corespund la o aciune utilizator

Task: create controller


def tesCreateStudent():
"""
Test store student
"""
rep = InMemoryRepository()
val = StudentValidator()
ctr = StudentController(rep, val)
st = ctr.createStudent("1", "Ion", "Adr")
assert st.getId()=="1"
assert st.getName()=="Ion"
try:
st = ctr.createStudent("1", "Vasile", "Adr")
assert False
except ValueError:
pass
try:
st = ctr.createStudent("1", "", "")
assert False
except ValueError:
pass
class StudentController:
"""
Use case controller for CRUD Operations on student
"""
def init (self, rep, validator):
self.rep = rep
self.validator = validator

def createStudent(self, id, name, adr):


"""
store a student
id, name, address of the student as strings
return the Student
raise ValueError if a student with this id already exists
raise ValueError if the student is invalid
"""
st = Student(id, name, adr)
if (self.validator!=None):
self.validator.validate(st)
self.rep.store(st)
return st

Application coordinator
Dependency injection (DI) este un principiu de proiectare pentru
sisteme orientat obiect care are ca scop reducerea cuplrii ntre
componentele sistemului.

De multe ori un obiect folosete (depinde de) rezultatele produse de alte


obiecte, alte pri ale sistemului.

Folosind DI, obiectul nu are nevoie s cunoasc modul n care alte pri ale
sistemului sunt implementate/create. Aceste dependene sunt oferite (sunt
injectate), inpreun cu un contract (specificaii) care descriu
comportamentul componentei
#create validator
validator = StudentValidator()
#crate repository
rep = InMemoryRepository(None)
#create console provide(inject) a validator and a repository
ctr = StudentController(rep, validator)
#create console provide controller
ui = Console(ctr)
ui.showUI()

Review aplicaia student manager de revazut abloanele ce apar


Arhitectur stratificat (Layered architecture)
Layer (strat) este un mecanism de structurare logic a elementelor ce compun un
sistem software
ntr-o arhitectur multi-strat, straturile sunt folosite pentru a aloca responsabiliti n
aplicaie.
Layer este un grup de clase (sau module) care au acelai set de dependene cu
alte module i se pot refolosi n circumstane similare.

User Interface Layer (View Layer, UI layer sau Presentation layer)


Application Layer (Service Layer sau GRASP Controller Layer)
Domain layer (Business Layer, Business logic Layer sau
Model Layer)
Infrastructure Layer (acces la date modaliti de
persisten, logging, network I/O ex. Trimitere de email, sau
alte servicii technice)
Aplicaia StudentCRUD

Review applicaie
Entiti

Entitate (Entity) este un obiect care este definit de identitatea lui (se identific
cu exact un obiect din lumea real).

Principala caracteristic a acestor obiecte nu este valoarea atributelor, este


faptul ca pe intreg existenta lor (in memorie, scris in fisier, incarcat, etc) se mentine
identitatea si trebuie asigurat consistenta (sa nu existe mai multe entitati care
descriu acelai obiect).

Pentru astfel de obiecte este foarte important sa se defineasc ce inseamn a fi egale.

def testIdentity():
#attributes may change
st = Student("1", "Ion", "Adr")
st2 = Student("1", "Ion",
"Adr2") assert st==st2

#is defined by its identity


st = Student("1", "Popescu", "Adr")
st2 = Student("2", "Popescu",
"Adr2") assert st!=st2
class Student:
def init (self, id, name, adr):
"""
Create a new student
id, name, address
String
"""
self. id = id
self. name = name
self. adr = adr

def eq (self,ot):
"""
Define equal for
students ot - student
return True if ot and the current instance represent the same student
"""
return self. id==ot. id

Atributele entitii se poat schimba dar identitatea rmbe acelai (pe ntreg
existena lui obiectul reprezint acelai obiect din lumea real )

O identitate greit conduce la date invalide (data corruption) i la inposibilitatea de


a implementa corect anumite operaii.
Obiecte valoare (Value Objects)

Obiecte valoare: obiecte ce descriu caracteristicile unui obiect din lumea


reala, conceptual ele nu au identitate.
Reprezint aspecte descriptive din domeniu. Cnd ne preocup doar atributele
unui obiect (nu i identitatea) clasificam aceste obiecte ca fiind Obiecte Valoare
(Value Object)

def testCreateStudent():
"""
Testing student creation
Feature 1 - add a student
Task 1 - Create student
"""
st = Student("1", "Ion", Address("Adr", 1, "Cluj"))
assert st.getId() == "1"
assert st.getName() == "Ion"
assert st.getAdr().getStreet()=="Adr"

st = Student("2", "Ion2", Address("Adr2", 1, "Cluj"))


assert st.getId() == "2"
assert st.getName() == "Ion2"
assert st.getAdr().getStreet() == "Adr2"
assert st.getAdr().getCity() == "Cluj"
class Address: class Student:
""" """
Represent an address Represent a student
""" """
def init (self,street,nr,city): def init (self, id, name, adr):
self. street = street """
self. nr = nr Create a new student
self. city = city id, name String
address - Address
def getStreet(self): """
return self. street self. id = id
self. name = name
def getNr(self): self. adr = adr
return self. nr
def getId(self):
def getCity(self): """
return self. city Getter method for id
"""
return self. id

Agregate i Repository

Grupai entiti i obiecte valoare n agregate. Alegei o entitate radcin (root) care
controleaz accesul la toate elementele din agregat.

Obiectele din afara agregatului ar trebui s aib referina doar la entitatea principal.

Repository creaz illuzia unei colecii de obiecte de acelai tip. Creai


Repository doar pentru entitatea principal din agregat

Doar StudentRepository (nu i AddressRepository)


Fiiere text n Python
Funcia Built in: open() returneaza un obiect reprezentnd fiierul
Cel mai frecvent se foloseste apelul cu dou argumente: open(filename,mode).
Filename un string, reprezint calea ctre fiier(absolut sau
relativ) Mode:
"r" open for read
"w" open for write (overwrites the existing content)
"a" open for append

Metode:
write(str) scrie string n fiier
readline() - citire linie cu line, returneaz string
read() - citete tot fiierul, returneaz string
close() - nchide fiier, elibereaz resursele
ocupate

Excepii:
IOError arunc aceast excepie daca apare o eroare de intrare/ieire (no file, no
disk space, etc)
Exemple Python cu fiiere text

#open file for write (overwrite if exists, create if not)


f = open("test.txt","w")
f.write("Test data\n")
f.close()

#open file for write (append if exist, create if not)


f = open("test.txt","a")
f.write("Test data line 2\n")
f.close()

#open for read


f = open("test.txt","r")
#read a line from the file
line = f.readline()
print line
f.close()

#open for read


f = open("test.txt","r")
#read a line from the file
line = f.readline().strip()
while line!="":
print line
line = f.readline().strip()
f.close()
#open for read
f = open("test.txt","r")
#read the entire content from the file
line = f.read()
print line
f.close()
#use a for loop
f = open("etc/test.txt")
for line in f:
print line
f.close()
Repository cu fiiere
class StudentFileRepository:
"""
Store/retrieve students from file
"""
def loadFromFile(self):
"""
Load students from file
"""
try:
f = open(self. fName, "r")
except IOError:
#file not
exist return
[]
line = f.readline().strip()
rez = []
while line!="":
attrs = line.split(";")
st = Student(attrs[0], attrs[1], Address(attrs[2], attrs[3],
attrs[4])) rez.append(st)
line = f.readline().strip()
f.close()
return rez

def store(self, st):


"""
Store the student to the file.Overwrite store
st - student
Post: student is stored to the file
raise DuplicatedIdException for duplicated id
"""
allS = self. loadFromFile()
if st in allS:
raise
DuplicatedIDException()
allS.append(st)
self. storeToFile(allS)

def storeToFile(self,sts):
"""
Store all the students in to the file
raise CorruptedFileException if we can not store to the file
"""
#open file (rewrite file)
f = open(self. fName, "w")
for st in sts:
strf = st.getId()+";"+st.getName()+";"
strf = strf + st.getAdr().getStreet()+";"+str(st.getAdr().getNr())
+";"+st.getAdr().getCity()
strf =
strf+"\n"
f.write(strf)
f.close()

Dynamic Typing
Verificarea tipului se efectueaza n timpul execuiei (runtime) nu n timpul
compilrii (compile-time).

n general n limbajele cu dynamic typing valorile au tip, dar variabilele nu. Variabila
poate referi o valoare de orice tip

Duck Typing

Duck typing este un stil de dynamic typing n care metodele i cmpurile


obiectelor determin semantica valid, nu relaia de motenire de la o clas anume
sau implementarea unei interfee.

Interfaa publica este dat de multimea metodelor i cmpurilor accesibile din


exterior. Dou clase pot avea acelai interfaa public chiar dac nu exista o relaie de
motenire de la o clas de baz comun

Duck test: When I see a bird that walks like a duck and swims like a duck and quacks
like a duck, I call that bird a duck

class Student: class Professor:


def init (self, id, name): def init (self, id, name, course):
self. name = name self. id = id
self. id = id self. name = name
def getId(self): self. course = course
return self. id
def getId(self):
def getName(self): return self. id
return self. name
def getName(self):
return self. name

def getCourse(self):
return self. course
l = [Student(1, "Ion"), Professor("1", "Popescu", "FP"), Student(31, "Ion2"),
Student(11, "Ion3"), Professor("2", "Popescu3", "asd")]

for el in l:
print el.getName()+" id "+str(el.getId())

def myPrint(st):
print el.getName(), " id ", el.getId()

for el in l:
myPrint(el)

Duck typing Repository

Fiindc interfaa public a clasei:


GradeRepository i GradeFileRepository
StudentRepository i StudentFileRepository
sunt identice controllerul funcioneaza cu oricare obiect, fr modificri.

#create a validator
val = StudentValidator()
#create repository
repo = StudentFileRepository("students.txt")
#create controller and inject dependencies
ctr = StudentController(val, repo)
#create Grade controller
gradeRepo = GradeFileRepository("grades.txt")
ctrgr = GradeController(gradeRepo, GradeValidator(), repo)
#create console ui and provide (inject) the controller
ui = ConsoleUI(ctr,ctrgr)
ui.startUI()
#create a validator
val = StudentValidator()
#create repository
repo = StudentRepository()
#create controller and inject dependencies
ctr = StudentController(val, repo)
#create Grade controller
gradeRepo = GradeRepository()
ctrgr = GradeController(gradeRepo, GradeValidator(), repo)
#create console ui and provide (inject) the controller
ui = ConsoleUI(ctr,ctrgr)
ui.startUI()
Asocieri ntre obiecte din domeniu
n lumea real, conceptual sunt multe relaii de tip many-to-many dar
modelarea acestor relaii n aplicaie nu este ntodeauna fezabil.

Cnd modeln obiecte din lumea real n aplicaiile noastre, asocierile


complic implementarea i ntreinerea aplicaiei.
Asocierile bidirecionale de exemplu presupun ca fiecare obiect din asociere
se poate folosi/nelege/refolosi doar npreun

Este important s simplificm aceste relaii ct de mult posibil, prin:


Impunerea unei direcii (transformare din bi-direcional n unidirecional)
Reducerea mutiplicitii
Eliminarea asocierilor ne-eseniale

Scopul este sa modelm lumea real ct mai fidel dar n acelai timp sa
simplificm modelul pentru a nu complica implementare.
Asocieri

Exemplu Catalog

gr = ctr.assign("1", "FP", 10) st = Student("1", "Ion",


assert gr.getDiscipline()=="FP" Address("Adr", 1, "Cluj"))
assert gr.getGrade()==10
assert gr.getStudent().getId()=="1" rep = GradeRepository()
assert gr.getStudent().getName()=="Ion" grades = rep.getAll(st)
assert grades[0].getStudent()==st
assert grades[0].getGrade()==10

Ascunderea detaliilor legate de persisten

Repository trebuie s ofere iluzia c obiectele sunt n memorie astfel codul


client poate ignora detaliile de implementare.
n cazul n care repository salveaz datele se n fiier, trebuie sa avem n vedere

anumite aspecte.

n exemplul de mai sus GradeRepository salveaz doar id-ul studentului (nu toate
campurile studentului) astfel nu se poate implementa o funcie getAll n care se
returneaza toate notele pentru toi studenii. Se poate n scimb oferi metoda
getAll(st) care returneaz toate notele pentru un student dat

def store(self, gr):


"""
Store a grade
post: grade is in the repository
raise GradeAlreadyAssigned exception if we already have a grade
for the student at the given discipline
raise RepositoryException if there is an IO error when writing to
the file
"""
if self.find(gr.getStudent(), gr.getDiscipline())!=None:
raise GradeAlreadyAssigned()

#open the file for append


try:
f = open(self. fname, "a")
grStr = gr.getStudent().getId()+","+gr.getDiscipline()
grStr =grStr+","+str(gr.getGrade())+"\n"
f.write(grStr)
f.close()
except IOError:
raise RepositorException("Unable to write a grade to the file")

Obiecte de transfer (DTO - Data transfer objects)

Funcionalitate: Primi 5 studeni la o disciplin. Prezentai in format tabelar :

nume student, nota la disciplina dat

Avem nevoie de obiecte speciale (obiecte de transfer) pentru acest caz de utilizare.
Funciile din repository nu ajung pentru a implementa (nu avem getAll()).
Se creaza o noua clas care conine exact informaiile de care e nevoie.
n repository:

DTO Data transfer obiect

n controller:
def getTop5(self,disc):
"""
Get the best 5 students at a given discipline
disc - string, discipline
return list of StudentGrade ordered descending on
the grade
"""
sds = self. grRep.getAllForDisc(disc)
#order based on the grade
sortedsds = sorted(sds, key=lambda
studentGrade: studentGrade.getGrade(),reverse=True)
#retain only the first 5
sortedsds =
sortedsds[:5]
#obtain the student names
for sd in sortedsds:
st = self.

stRep.find(sd.getStudentID())
sd.setStudentName(st.getName())
return sortedsds

Motenire

Motenirea permite definirea de clase noi (clase derivate) reutiliznd clase existente (clasa de
baz). Clasa nou creat moteneste comportamentul (metode) i caracteristicile (variabile membre,
starea) de la clasa de baz

Dac A i B sunt dou clase unde B moteneste de la clasa A (B este derivat din clasa A sau clasa B este
o specializare a clasei A) atunci:
clasa B are toate metodele si variabilele membre din clasa A
clasa B poate redefini metode din clasa A
clasa B poate adauga noi membrii (variabile, metode) pe lng cele motenite de la clasa A.

Reutilizare de cod
Una din motivele pentru care folosim motenire este reutilizarea codului existent ntr-o clas (motenire de
implementare).

Comporamentul unei clase de baze se poate moteni de clasele derivate.


Clasa dericvat poate:
poate lsa metoda nemodificat
apela metoda din clasa de baz
poate modifica (suprascrie) o metod.

Motenire n Python
Syntax:
class DerivedClassName(BaseClassName):

Clasa derivat motenete:


cmpuri
metode

Dac acessm un membru (cmp, metod) : se caut n clasa curent, dac nu se gsete
atunci cautarea continu n clasa de baz
class B(A): class A:
""" def init (self):
This class extends A print "Initialise
A is the base class, A"
B is the derived class
B is inheriting everything from class A def f(self):
""" print "in method f from A"
def init (self):
#initialise the base class def g(self):
A. init (self) print "in method g from A"
print "Initialise B"

def g(self):
"""
Overwrite method g from A
"""
#we may invoke the function from the
base class
A.f(self)
print "in method g from B"

b = B()
#f is inherited from A
b.f()
b.g()
Clasele Derivate pot suprascrie metodele clasei de baza.

Suprascrierea poate nlocui cu totul metoda din clasa de baz sau poate extinde funcionalitatea
(se execut i metoda din clasa de baz dar se mai adaug cod)

O metod simpl s apelm o metod n clasa de baz:


BaseClassName.methodname (self,arguments)

Diagrame UML Generalizare (motenire)

Relaia de generalizare ("is a") indic faptul c o clas (clasa derivat) este o
specializare a altei clase (clasa de baz). Clasa de baz este generalizarea clasei
derivate.

Orice instan a clasei derivate este si o instana a clasei de baz.

Repository cu Fiiere
class StudentFileRepository(StudentRepository):
"""
Repository for students (stored in a file)
"""
pass
class StudentFileRepository(StudentRepository):
"""
Store/retrieve students from file
"""
def init (self,fileN):
#properly initialise the base class
StudentRepository. init (self) self.
fName = fileN
#load student from the file
self. loadFromFile()

def loadFromFile(self):
"""
Load students from file
raise ValueError if there is an error when reading from the file
"""
try:
f = open(self. fName,"r")
except IOError:
#file not exist
return
line = f.readline().strip()
while line!="":
attrs = line.split(";")
st = Student(attrs[0],attrs[1],Address(attrs[2], attrs[3], attrs[4]))
StudentRepository.store(self, st)
line = f.readline().strip()
f.close()

Suprascriere metode

def testStore():
fileName = "teststudent.txt"
repo = StudentFileRepository(fileName)
repo.removeAll()

st = Student("1","Ion",Address("str",3,"Cluj"))
repo.store(st)
assert repo.size()==1 assert
repo.find("1") == st
#verify if the student is stored in the file
repo2 = StudentFileRepository(fileName)
assert repo2.size()==1
assert repo2.find("1") == st
def store(self,st):
"""
Store the student to the file.Overwrite store
st - student
Post: student is stored to the file
raise DuplicatedIdException for duplicated id
"""
StudentRepository.store(self, st)
self. storeToFile()

def storeToFile(self):
"""
Store all the students in to the file
raise CorruptedFileException if we can not store to the file
"""
f = open(self. fName,"w")
sts = StudentRepository.getAll(self)
for st in sts:
strf = st.getId()+";"+st.getName()+";"
strf = strf + st.getAdr().getStreet()
+";"+str(st.getAdr().getNr()) +";"+st.getAdr().getCity()
strf = strf+"\n"
f.write(strf)
f.close()

Excepii
def createdStudent(self):
"""
Read a student and store in the apllication
"""
id = input("Student id:").strip() name
= input("Student name:").strip()
street = input("Address - street:").strip()
nr = input("Address - number:").strip() city
= input("Address - city:").strip() try:
self. ctr.create(id, name,street,nr,city)
except ValueError as msg:
print (msg)

def createdStudent(self):
"""
Read a student and store in the apllication
"""
id = input("Student id:").strip() name
= input("Student name:").strip()
street = input("Address - street:").strip()
nr = input("Address - number:").strip() city
= input("Address - city:").strip() try:
self. ctr.create(id, name,street,nr,city)
except ValidationException as ex:
print (ex)
except DuplicatedIDException as ex:
print (ex)
class ValidationException(Exception):
def init (self,msgs):
"""
Initialise
msg is a list of strings (errors)
"""
self. msgs = msgs
def getMsgs(self):
return self. msgs

def str (self):


return str(self. msgs)
Ierarhie de excepii

class StudentCRUDException(Exception):
pass
class ValidationException(StudentCRUDException):
def init (self,msgs):
"""
Initialise
msg is a list of strings (errors)
"""
self. msgs = msgs
def getMsgs(self):
return self. msgs
def str (self):
return str(self. msgs)

class RepositorException(StudentCRUDException):
"""
Base class for the exceptions in the repository
"""
def init (self, msg):
self. msg = msg
def getMsg(self):
return self. msg
def str (self):
return self. msg
class DuplicatedIDException(RepositorException):
def init (self):
RepositorException. init (self, "Duplicated ID")
def createdStudent(self):
"""
Read a student and store in the apllication
"""
id = input("Student id:").strip() name
= input("Student name:").strip()
street = input("Address - street:").strip()
nr = input("Address - number:").strip() city
= input("Address - city:").strip() try:
self. ctr.create(id, name,street,nr,city)
except StudentCRUDException as ex:
print (ex)

Layered arhitecture Structur proiect


Layered architecture GUI Example
Tkinter este un toolkit GUI pentru Python (este disponibil pe majoritatea platformelor Unix , pe
Windows i Mac)

Review - aplicaia StudentCRUD cu GUI

Tkinter (sau orice alt GUI ) nu se cere la examen

Testarea programelor
Testarea este observarea comportamentului unui program n multiple execuii.
Se execut programul pentru ceva date de intrare i se verific daca rezultate sunt corecte n
raport cu intrrile.
Testarea nu demonstreaz corectitudinea unui program (doar ofer o anumit sigurana ,
confiden). In general prin testare putem demonstra c un program nu este corect, gsind un
exemplu de intrri pentru care rezultatele sunt greite.

Testarea nu poate identifica toate erorile din program.

Metode de testare
Testare exhaustiv
Verificarea programului pentru toate posibilele intrri.
Imposibil de aplicat n practiv, avem nevoie de un numr finit de cazuri de testare.

Black box testing (metoda cutiei negre)


Datele de test se selecteaza analiznd specificaiile (nu ne uitm la implementare).
Se verific dac programul respect specificaiile.
Se aleg cazur de testare pentru: valori obinuite, valori limite, condiii de eroare.

White box testing (metoa cutiei transparente)


Datele de test se aleg analiznd coul surs. Alegem datele astfel nct se acoper toate ramurile
de execuie (n urma executri testelor, fiecare instruciune din program este executat mcar
odat)

White box vs Black Box testing


def isPrime(nr):
"""
Verify if a number is prime
return True if nr is prime False if not
raise ValueError if nr<=0
"""
if nr<=0:
raise ValueError("nr need to be positive")
if nr==1:#1 is not a prime number
return False
if nr<=3:
return True
for i in range(2,nr):
if nr%i==0:
return False
return True
Black Box White Box (cover all the paths)
test case pentru prim/compus test case pt 0
test case pentr 0 test case pt negative
test case pentru numere test case pt 1
negative test case pt 3
test case pt prime (fr divizor)
test case pt neprime
def blackBoxPrimeTest(): def whiteBoxPrimeTest():
assert (isPrime(5)==True) assert (isPrime(1)==False)
assert (isPrime(9)==False) assert (isPrime(3)==True)
try: assert (isPrime(11)==True)
isPrime(-2) assert (isPrime(9)==True)
assert False try:
except ValueError: isPrime(-2)
assert True assert False
try: except ValueError:
isPrime(0) assert True
assert False try:
except ValueError: isPrime(0)
assert True assert False
except ValueError:
assert True

Nivele de testare
Testele se pot categoriza n funcie de momentul n care se creaz (n cadrul procesului de
dezvoltare) sau n funcie de specificitatea testelor.

Unit testing
Se refer la testarea unei funcionalitai izolate, n general se refer la testarea la nivel de
metode. Se testeaz funciile sau pri ale programului, independent de restul applicaiei

Integration testing
Consider ntreaga aplicaie ca un ntreg. Dup ce toate funciile au fost testate este nevoie de
testarea comportamentului general al programului.

Testare automat (Automated testing)


Testare automat presupune scrierea de programe care realizeaz testarea (n loc s se
efectueze manual).
Practic se scrie cod care compara rezultatele efective pentru un set de intrri cu rezultatele
ateptate.
TDD:
Paii TDD:

teste automate
scrierea specificaiilor (inv, pre/post, excepii)
implementarea codului
/shutdown) necesare pentru teste
PyUni fixture
agregarea testelor
t test suite
- independena testelor fa de modalitatea de raportare
biblio
import unittest
tec class TestCaseStudentController(unittest.TestCase):
Pytho def setUp(self):
#code executed before every testMethod
n val=StudentValidator()
pentr self.ctr=StudentController(val, StudentRepository())
st = self.ctr.create("1", "Ion", "Adr", 1, "Cluj")
u unit
def tearDown(self):
testin #cleanup code executed after every testMethod
g
def testCreate(self):
modulul self.assertTrue(self.ctr.getNrStudents()==1)
unittest #test for an invalid student
ofer: self.assertRaises(ValidationEx,self.ctr.create,"1", "", "", 1, "Cj")
test
e #test for duplicated id
aut self.assertRaises(DuplicatedIDException,self.ctr.create,"1", "I",
om "A", 1, "j")
ate
mo def testRemove(self):
dali #test for an invalid id
tat self.assertRaises(ValueError,self.ctr.remove,"2")
e
uni self.assertTrue(self.ctr.getNrStudents()==1)
for
m st = self.ctr.remove("1")
de self.assertTrue(self.ctr.getNrStudents()==0)
pre self.assertEquals(st.getId(),"1")
gat self.assertTrue(st.getName()=="Ion")
ire/ self.assertTrue(st.getAdr().getStreet()=="Adr")
cur
aa
re if name == ' main ':
(se unittest.main()
tup
Depa depanare n cercm s identificm cauza erorii, modaliti de rezolvare. Scopul
este sa elminm eroarea.
nare Se pate realiz folosind:
(Debu instructiuni print
gging instrumente specializate oferite de IDE

) Depanarea este o activitate neplcut, pe ct posibil, trebuie evitat.

Depanar Perspectiva Eclipse pentru depanare


eaDebug
ging este
activitate Debug view
a prin prezint starea curent de execuie (stack trace)
care execuie pas cu pas, resume/pause
reparm Variables view
erorile inspectarea variabilelor
gsite n
urma Inspectarea programelor
testrii. Any fool can write code that a computer can understand. Good programmers
Dac write code that humans can understand
testarea
indic Prin stilul de programare nelegem toate activitile legate de scrierea de programe
prezena i modalitile prin care obinem cod: uor de citit, uor de neles, uor de intreinut.
unei erori
atunci
Stil de programare
prin
Principalul atribut al codului surs este considerat uurina de a citi (readability).
Un rea textului (indentare, white spaces)
progra specificaii
m, ca denumiri sugestive (pentru clase, funcii, variabile) din program
i denumiri sugestive
orice folosirea conveniilor de nume
public
aie,
este
un text
Convenii de nume (naming conventions):
care
trebui clase: Student, StudentRepository
e citit variabile: student, nrElem (nr_elem)
i funcii: getName, getAddress, storeStudent
neles (get_name,get_address, store_student)
cu constante: MAX
uurin
de Este inportant sa folosii acelai reguli de denumire in toat aplicaia
orice
progra Recursivitate
mator.
Elementel O noiune e recursiv dac e folosit n propria sa definiie.
e stilului
de
programar O funcie recursiv: funcie care se auto-apeleaz.
e sunt:
Rezultatul este opinut apelnd acelai funcie dar cu ali
c
o parametrii

def factorial(n):
"""
compute the factorial
n is a positive
integer return n!
"""
if n== 0:
return 1
return factorial(n-1)*n
m
e
n
t Recursivitate direct: P apeleaz P
a
r Recursivitate indirect: P apeleaz Q, Q apeleaz P
i
i
f Cum rezolvm probleme folosind recursivitatea:
o
r Definim cazul de baz: soluia cea mai simpl.
m Punctul n care problema devine trivial (unde se
a
t oprete apelul recursiv)
a Pas inductiv: nprim problema ntr-o variant mai
s robleme plus ceva pai simplii
i ex. apel cu n-1, sau doua apeluri recusive cu n/2
m

def recursiveSum(l):
"""
def fibonacci(n):
Compute the sum of
numbers l - list of """
number compute the fibonacci
number n - a positive
return int, the sum of
numbers """ integer
#base case return the fibonacci number for a given
if l==[]: n """
return 0 #base case
#inductive step if n==0 or n==1:
return 1
return l[0]+recursiveSum(l[1:])
#inductive step
p return fibonacci(n-1)+fibonacci(n-2)
l
Obs recursiveSum(l[1:]):
l[1:] - creaz o copie a listei l
a exerciiu: modificai funcia recursiveSum pentru a evita
l l[1:]

a Recursivitate n python:
c la fiecare apel de metod se creaz o noua tabel de
e simboluri (un nou namespace). Aceast tabel conine
l valorile pentru parametrii i pentru variabilele locale
e tabela de simboluri este salvat pe stack, cnd apelul se
a termin tabela se elimin din stiv

def isPalindrome(str):
"""
verify if a string is a
palindrome str - string
return True if the string is a palindrome False otherwise
"""
dict = locals()
print id(dict)
print dict

if len(str)==0 or len(str)==1:
return True

return str[0]==str[-1] and isPalindrome(str[1:-1])

i
Recursivitate
p
Avantaj fiecare recursie se creaz o nou tabel de simboluri
e:
cl Analiza complexitii
ari
tat Analiza complexiti studiul eficienei algoritmilor.
e
co
d Eficiena algoritmilor n raport cu:
m timpul de execuie necesar pentru rularea programului
ai spaiu necesar de memorie
si
m
Timp de execuie, depinde de:
pl
algoritmul folosit
u
datele de intrare
Dezava hardwareul folosit
ntaje: sistemul de operare (apar diferene de la o rulare la alta).
co
ns Exemplu timp de execuie
u def fibonacci(n): def fibonacci2(n):
m """
compute the fibonacci number
"""
compute the fibonacci numb
de n - a positive integer n - a positive integer
return the fibonacci number for a given n return the fibonacci numbe
m """ """
e #base case
if n==0 or n==1:
sum1 = 1
sum2 = 1
m return 1 rez = 0
#inductive step for i in range(2, n+1):
ori return fibonacci(n-1)+fibonacci(n-2) rez = sum1+sum2
sum1 = sum2
e sum2 = rez
m return rez

ai def sw measureFibo(nr):
= StopWatch()
m print "fibonacci2(", nr, ") =", fibonacci2(nr) print
"fibonacci2 take " +str(sw.stop())+" seconds"
ar
sw = StopWatch()
e print "fibonacci(", nr, ") =", fibonacci(nr)
p print "fibonacci take " +str(sw.stop())+" seconds"

e measureFibo(32)
n fibonacci2( 32 ) = 3524578
t fibonacci2 take 0.0 seconds
fibonacci( 32 ) = 3524578 fibonacci
r take 1.7610001564 seconds
u
fiind cantitatea de resurse utilizate de algoritm (timp, memorie).
Eficiena
Msurarea eficienei:
algoritmilor analiz matematic a
E algoritmului - analiz
fi asimptotic Descrie eficiena
c sub forma unei funcii
i matematice. Estimeaza timpul
e de execuie pentru toate intrrile
n pisibile.
o analiz empiric a algoritmului
a
determinarea timpului exact de
a
l execuie pentru date specifice nu
g putem prezice timpul pentru toate
o datele de intrare.
ri
Timpul de execuie pentru un algoritm este studiat n relaie cu
t dimensiunea datelor de intrare.
m Estimm timpul de execuie n funcie de dimensiunea datelor.
il
Realizm o analiz asymptotic. Determinm ordinul de mrime
o
pentru resursa utilizat (timp, memorie), ne intereseaz n special
r
pentru cazurile n care datele de inrare sunt mari
p
o
Complexitate
a
t caz favorabil - datele de intrare care conduc la timp de execuie
minim
e
fi best-case complexity (BC):
d
e
fi
n
it

c
a
BC( A) min E(I )
ID

caz defavorabil date worst-case complexity (WC): WC(A)


de intrare unde avem I
max DE(I )
cel mai mare timp de
execuie.
caz mediu - timp de execuie.
average complexity (AC):

AC( A) P(I )E(I )


ID

A - algoritm; multimea tutoror datelor de intrare


E(I) numr de posibile pentru un n fixat
operaii; P(I)
probabilitatea de Obs. Dimensiunea datelor (n) este fixat (un numar
a avea I ca i mare) caz favorabil/caz defavorabil se refer la un
date de intrare D anumit aranjament al datelor de intrare care
produc timp minim/maxim

Complexitate timp de execuie

numrm pai (operaii elementare) efectuai (de exemplu numrul de


instruciuni, numr de comparaii, numr de adunri).
numrul de pai nu este un numr fixat, este o funcie, notat T(n), este in
funcie de dimensiunea datelor (n), nu rezult timpul exact de execuie
Se surprinde doar esenialul: cum crete timpul de execuie n funcie de
dimensiunea datelor.
Ne ofer ordinea de mrime pentru timpul de execuie (dac
n , then 3 n 2 n 2 ).
putem ignora constante mici dac n
aceste constante nu afecteaz ordinea de mrime.
Ex : T (n) 13 n3 42 n2 2 n log n 3 n
2

Fiindc
0 log 2 n n,

n 1 i

n n,
n 1, putem conclude c temenul n3
domin aceast
expre mare r im execuie a algoritmului
sie Ca e pu crete cu ordinul lui
n , ceea se
3
cand ur , l
n este t de scrie sub
ma forma
3
T (n) O(n ) i se citete T(n) este de ordinul n3

n continuare, vom nota prin f o funcie de excuie a unui algoritm, T : N N .


f:N
i prin T funcia care d complexitatea timp

Definiia 1 (Notaia O , Big-oh). Spunem c


T (n) O( f (n))

dac exist c i n0 constante pozitive


(care nu depind de n) astfel nct
0 T (n) c n n0 .
f (n),
Cu alte cuvinte, notaia O d marginea
superioar
Definiia alternativ: Spunem c T (n) O( f (n))

dac

T (n)
lim f (n)
n

este 0 sau o constant, dar nu .

Observaii.

1. Dac T (n) 13 n3 42 n2 2 n log

n3

n , atunci

T (n)
lim 13 . Deci, putem spune c
2 3
n
3 n
T (n) O(n ) .

2. Notaia O este bun pentru a da o limit superioar unei


funcii. Observm, totui, c dac
3
T (n) O(n ) , atunci este i
4
O(n ) ,
5
O(n ) , etc atta timp ct limita este 0. Din aceast cauz
avem nevoie de o in rioar a complexitii. Aceast notaie este
notaie pentru limita fe .

Definiia 2 (Notaia , Big-omega). Spunem c


T (n) ( f (n))
dac exist c i n0 constante
pozitive (care nu depind de n) astfel nct T (n),
0 c f (n)
n n0 .

notaia d marginea inferioar

Definiia alternativ: Spunem c T (n) ( f (n))

dac
T (n)
lim f (n)
n

este o constant sau , dar nu 0.


T dac T (n) O( f (n))
(n)
Definiia 3 (Notaia , Big-
theta). Spunem c (
f
(n)
)
i dac
T (n) ( f (n)) , altfel spus dac exist c1, c2 i n0 po ve (care de n)
constante ziti nu depind astfel
nct
c1 f (n) T (n) c2 f (n),

n n0 .

notaia mrginete o funcie pn la factori


constani

Definiia alternativ Spunem c sau ).

T (n) ( f (n))

dac
T (n)
lim f (n)
n

este o constant nenul (dar nu 0

Observaii.
1. Timpul de execuie al unui algoritm este ( f (n))

dac i numai dac timpul su de execuie n


cazul cel mai defavorabil este O( f (n))
( f (n)) .
i timpul su de execuie n cazul cel mai favorabil este
2. Notaia O( f este de cele mai multe ori folosit n locul
(n)) notaiei ( f (n)) .
3. Dac
T (n) n 3
313
n
422
n 2
n
log
T
T (n) (n ) . Acest
3
n , atunci lim
(n)

13 .
Deci
,
2
lucru poate fi fap c T (n) (n) (n
3

3
n dedus i din tul O(n ) i T ).
3
n

Sume

for i in range(0, n):


#some instructions

presupunnd c ceea ce este n corpul structurii repetitive (*) se execut n f(i) pai
timpul de execuie al ntregii structuri repetitive poate fi estimat astfel
n

T (n)
f (i)
i 1

Se poate observa c, n cazul n care se folosesc bucle imbricate, vor rezulta


sume imbricate.
n continuare, vom prezenta cteva dintre sumele uzuale:
Calculul se efectueaza astfel:
se simplific sumele eliminm constantele, separm termenii in sume
individuale
facem calculul pentru sumele simplificate.

Exemple cu sume
Analizai complexitatea ca timp de execuie pentru urmtoarele funcii
def f1(n): n
s = 0 T ni1 1n T nn
for i in range(1,n+1):
s=s+i Complexitate (Overall complexity) n
return s
Cazurile Favorabil/Mediu/Defavorabil sunt identice
def f2(n): n
i = 0 while T ni1 1n T nn
i<=n:
#atomic operation
i = i + 1 Overall complexity n
Cazurile Favorabil/Mediu/Defavorabil sunt identice
def f3(l): Caz favorabil:
"""
l list of numbers primul element e numr par: T n1 1
return True if the list contains
an even nr Caz defavorabil:
""" Nu avem numere pare n list: T nn n
poz = 0
while poz<len(l) and l[poz]%2 !=0: Caz mediu:
poz = poz+1 While poate fi executat 1,2,..n ori (acelai probabilita
return poz<len(l)
Numrul de pai = numrul mediu de iteraii

T n12...n nn12 T n n

Complexitate O n

Exemple cu sume
2n2 2n 2n2
def f4(n): T ni1 1i 1 2ni1
for i in range(1,2*n-2): ji2
for j in range(i+2,2*n):
#some computation 2n2 2n2 2n2
pass T ni1 2n i1 ii1 1

2n2

T n2n i1 1 2n22n 1 22n2



2 2
T n2n 3n1 n Overall complexity n2
def f5(): Caz favorabil: While se execut odat
for i in range(1,2*n-2): 2n2
j = i+1
cond = True T ni1 12n2 n
while j<2*n and cond: Caz defavorabil: While executat 2n i1 ori
#elementary operation
T n2n2 2n i1...2n 3n1 n
2 2
if someCond:
i1
cond = False
Caz mediu:
Pentru un i fixat While poate fi executat 1,2..2n-i-1 ori
numr mediu de pai:
Ci12...2ni1 2ni1...2ni 2
2n2 2n2
T n Ci
2
2ni 2...n
i1 i1

Overall complexity O n2
Formule cu sume:

1 n
i 1

suma constant.


i 1

i 1

n(n 1)
i 2

n(n 1)(2n 1)
i2 2

suma liniar (progresia aritmetic) suma

ptratic
1
nln(n) O(1)

i
i 1

c
i
i 1

n 1
c 1,
c1

c1
suma armonic

progresia geometric (crete exponenial)


uzual T )
Compl e (n)

exiti O(1
- i p constant. It is a great complexity. This means that
t m the algorithm takes only constant time.

T (n) O(log2 log2 n)

- timp foarte rapid (aproape la fel de rapid ca un timp constant)

T (n) O(log2 n)
- complexitate logaritmic:
timp foarte bun (este ceea ce cutm, n general, pentru orice algoritm);
log2 1000 complexitate cutare binar,
log2 1.000.000 nlimea unei arbore binar echilibrat
20 ;
k
T (n) O((log n) )
2
- unde k este factor constant; se numete complexitate polilogaritmic
(este destul de bun);
- complexitate liniar;
Complexit
i uzuale

T (n) O(n)

T (n) O(n log2 n)

- este o complexitate faimoas, ntlnit mai ales la sortri (MergeSort,


QuickSort);

2
T (n) O(n )
- este complexitate ptratic (cuadratic);
dac n este de ordinul milioanelor, nu este prea bun;
k
T (n) O(n )
- unde k este constant; este complexitatea polinomial (este
practic dora daca k nu este prea mare);

n 3
T (n) O(2 ), O(n ), O(n!)
- complexitate exponenial (algoritmii cu astfel de complexiti sunt
practici
n 10, n 20 ).
doar
pentru
valori
mici ale
lui
h 1
N (h) 3 N (0) (1 3
Recurene 3 ... 3 ) 3
2 h 1 i

i 0

O recuren este o formul matematic definit


recursiv.
Ex. numrul de noduri (notat N(h)) dintr-un arbore ternar
complet de nlime h ar putea fi descris sub forma
urmtoarei formule de recuren:
N (0) 1

N (h) 3 N (h 1)
1,

h1

Explicaia ar fi urmtoarea:
Numrul de noduri dintr-un arbore ternar complet
de nlime 0 este 1.
Numrul de noduri dintr-un arbore ternar
complet de nlime h se obine ca fiind de 3 ori
numrul de noduri din subarborele de nlime
h-1, la care se mai adaug un nod (rdcina
arborelui).
Dac ar fi s rezolvm recurena, am obine c
numrul de noduri din arborele ternar complet

de nlime h este

Exemple
def recursiveSummemorie necesar algoritmului pentru stocarea datelor de intrare, a
"""
Compute the rezultatelor finale i a rezultatelor intermediare. Se estimeaz, ca i
sum of numbers
l - list of number
return int, timpul
the sumdeofexecuie al unui algoritm, n notaiile
numbers
"""
#base case O , , .
if l==[]:
return
#inductive step
return l[0
Toate obsevaiile referitoare la notaia asimptotic a complexitii
def hanoi(n, x, ca
y,timp
z): de execuie sunt valabile i pentru complexitatea ca spaiu
"""
n -number of disk on the x
stick de memorare.
x - source
y - destination stick z
"""
- intermediate
Exemplu
if n==1:
Analizai complexitatea ca spaiu de memorare pentru
print "disk 1 from"
return
hanoi(n-1, x, z, y)
urmtoarele funcii
print "disk "
hanoi(n-1, z,defy,iterativeSum(l):
x)
"""
Avem nevoie de spaiu pen
Compute the sum of numbers
l - list of number T nn n
return int, the sum of numbers
"""
Comp rez = 0
for nr in l:
lexita rez = rez+nr
return rez
tea
spaiu
def recursiveSum(l):
"""
Compute the sum of numbers
Recuren: T n T n11
0 for

de l - list of number
return int, the sum of numbers
memo """
#base case
rare if l==[]:
return 0
#inductive step
return l[0]+recursiveSum(l[1:])
Compl
exitatea unui Analza complexitii (timp/spaiu)
algoritm din
punct de
pentru o funcie
vedere al 1 Dac exist caz favorabil/defavorabil:
spaiului de descrie Caz favorabil
memorare calculeaz complexitatea pentru Best Case
estimeaz descrie Worst Case
cantitatea de calculeaz complexitatea pentru Worst case
calculea egaliti
z altfel
comple
xitatea calculeaz folosind sume
medie
calculea
Algoritmi de cutare
z
comple datele sunt n memorie, o secven de
xitatea
general nregistrri (k1, k2, ..., kn )

se caut o nregistrare avnd un cmp
2 Dac
Favorabil
egal cu o valoare dat
= cheia de cutare.
Defavorabi
l = Mediu Dac am gsit nregistrarea, se returneaz
(nu avem
cazuri poziia nregistrrii n secven
favorabile/
defavorabil
dac cheile sunt ordonate atunci ne
e) intereseaz poziia n care trebuie inserat
calculea
z
o nregistare nou astfel nct ordinea se
comple menine
xitatea

Specificaii pentru cutare:


Calculeaz
complexitate
a:
dac
avem
Date:
recuren a,

calc n,
ulea
z
(ki
folos ,
ind
i
= n
0
, N,
n n
- ;
1 Rezultate: p;
)
Post-condiii: (0pn-1 and a
;
kp)

P
or (p=-1 dac cheia nu
r
exist).
e
c Cutare secvenial cheile nu sunt
o ordonate
n def searchSeq(el,l): def searchSucc(el,l):
""" """
d Search for an element in a list
el - element
Search for an elemen
el - element
l - list of elements l - list of elements
i return the position of the element
or -1 if the element is not in l
return the position
or -1 if the e
""" """
poz = -1
for i in range(0,len(l)):
i = 0
while i<len(l) and el!
if el==l[i]: i=i+1
i poz = i
return poz
if i<len(l):
return i
return -1
i
:
T nn1 1
i0 i: nN, n, and k0 < k1 < .... < kn-1 ;
Rezultate p;
Post-condiii: (p=0 and a k0) or
(p=n and a > kn-1) or
Spec
((0<pn-1) and (kp-1 < a
ifica
ii kp)).
pent
Cutare secvenial chei ordonate
ru
cut def searchSeq(el,l): def searchSucc(el,l):

are """
Search for an element in a list
el - element
"""
Search for an elemen
el - element
chei l - list of ordered elements
return the position of first occurrence
l - list of ordered
return the pos
ordo or the position where the element
can be inserted
occurrence or
the element ca

nate: """
if len(l)==0:
return 0
"""
if len(l)==0:
return 0
poz = -1 if el<=l[0]:
for i in range(0,len(l)): return 0
if el<=l[i]: if el>=l[len(l)-1]:
poz = i return len(l)
if poz==-1: i = 0
return len(l) while i<len(l) and el>
return poz i=i+1
return i
Date
T nn1 1n n Best case: the element is at the f
a,n, i0 T n1
Worst-case: the element is in the
(ki, T nn
i=0, Average case: while can be exec
T n12...n1 n
n-1); Overall complexity O n

PAl
reco go
ndii ri
t
m cesiv toate cheile
i cheile nu sunt ordonate
d cutare binar
e folosete divide and conquer

cheile sunt ordonate


c
Cutare binar (recursiv)
u def binaryS(el, l, left, right):
"""
S
t e
a
a r
c

r h

a
e n

e
l
e
m
e
n
t

i
cutar n

e a

secven l
i
s
ial t

e
se l

ex -

am e
l
e
ine m
e
az n
t

suc t
o
b in which
e we search
return the position of first
s occurrence or the insert position
e """
a if
r l
c e
h f
e t
d >
l =
r
- i
g
a h
t
l -
i 1
s :
t
r
o e
f t
u
o r
r n
d
e r
i
r
g
e
h
d
t
m
e
l =
e
m (
e l
n e
t f
s t
+
l r
e i
f g
t h
, t
r )
i /
g 2
h
t i
f
t
h e
e l
<
s =
u l
b [
l m
i ]
s :
t r
etu
r
n a

b l
i i
n s
a t
r
y e
S l
(
e -
l
, e
l
l e
, m
e
l n
e t
f
t t
, o
m b
)
e
e
s
l
s e
e a
: r
c
retur
h
n
binar e
yS(el d
, l, l - a list of ordered elements
m, return the position of first
right occurrence or the insert position
) """
if len(l)==0:
def r
searchBinaryR e
t
ec(el, l):
u
""" r
S n
e
a 0
r
c i
h f
a e
n l
<
e l
l [
e 0
m ]
e :
n return 0
t if
e
i l
n >
l
[ n
T
l
e


n
(
l
)
-
1
]
:

r
e
t
u
r
n

l
e
n
(
l
)
return
binaryS(e
l, l, 0,
len(l))

Recuren

cutare
binar


if
n
T
(
n



(1), otherwise


Cutare binar (iterativ)
def searchBinaryNonRec(el, l):
"""
Search an element in a
list el - element to be
searched
l - a list of ordered elements
return the position of first occurrence or the position where the element can be
inserted
"""
if len(l)==0:
return 0
if el<=l[0]:
return 0
if el>=l[len(l)-1]:
return len(l)
right=len(l)
left = 0
while right-left>1:
m = (left+right)/2
if el<=l[m]:
right=m
else:
left=m
return right

Complexitate

Timp de execuie
Algoritm best worst case average overall
case
SearchSeq (n) (n) (n) (n)

SearchSucc (1) (n) (n) O(n)


SearchBin (1) (log 2 n) (log 2 n) Olog2 n

Vizualizare cautri

Cutare in python - index()


l = range(1,10)
try:
poz = l.index(11)
except ValueError:
# element is not in the list

- eq

Searching in python- in
l = range(1,10)
found = 4 in l

iterable (definii iter and next )


class MyClass2:
def init (self):
self.l = []

def add(self,obj):
self.l.append(obj)

def iter (self):


"""
Return an iterator object """
self.iterPoz = 0 return self

#we can use any iterable in a for container = MyClass2()


for el in container: print (el)

def next (self):


"""
Return the next element in the iteration raise StopIteration
exception if we are at the end
"""
if (self.iterPoz>=len(self.l)): raise StopIteration()

rez = self.l[self.iterPoz] self.iterPoz =


self.iterPoz +1 return rez

def testIn():
container = MyClass2() for i in
range(0,200):
container.add(MyClass(i,"ad")) findObj =
MyClass(20,"asdasd") print findObj in container
search 200 search 100
BinaryRec in 0.000000 sec; poz=200 Binary
Performan - PythIndex in 0.000000 sec; poz=200
PythonIn in 0.000000 sec BinaryNon
PythIn
Pytho
cutare in 0.000000 sec; poz=200 searchSuc
in 0.000000 sec; poz=200
Binary
search

def measureBinary(e, l):


sw = StopWatch()
Sortare
poz = searchBinaryRec(e, l)
print ("
Rearanjarea datelor
BinaryRec in %f sec; poz=%i"
dintr-o colecie astfel nct o
cheie verific o relaie de ordine dat
def measurePythonIndex(e, l):
sw = StopWatch()
poz = -2
try:
poz = l.index(e)
internal sorting - datele sunt n memorie
except ValueError:
pass #we ignore the error.. external sorting - datele sunt n fiier
print (" PythIndex in %f sec; poz=%i"

def measureSearchSeq(e, l):


sw = StopWatch() Elementele coleciei sunt nregistrri, o
poz = searchSeq(e, l) nregistrare are una sau mai multe cmpuri
print (" searchSeq in %f sec; poz=%i"
Cheia K este asociat cu fiecare nregistrare, n
general este un cmp.

Colecia este sortat:


cresctor dup cheia k
K : if K(i) K(j) for 0
i<j<n 2

descresctor: if K(i) ,
K(j) for 0 i<j<n
.
Sortare intern
n memorie .
.
,
Dat
k
e n

)
n }
,
K P
; r
e
{ c
K o
= n
( d
k i
1

,
i
i permutare al lui
: K, avnd
elementele sortate,
k k'1 k'2 ... k'n.
i
Sortare prin selecie (Selection
Sort)
R
,
se determin elementul avnd cea mai
i mica cheie, interschimbare elementul
cu elementul de pe prima poziie
=
reluat procedura penru restul de
1 elemente pn cnd toate elementele au
, fost considerate.

n Sortare prin selecie


Rezultate K';
Post-condiii:

K'

e
def selectionSort(l):
"""
sort the element of the
list l - list of element
return the ordered list (l[0]<l[1]<...)
"""
for i in range(0,len(l)-1):
ind = i
#find the smallest element in the rest of the list
for j in range(i+1,len(l)):
if (l[j]<l[ind]):
ind =j
if (i<ind):
#interchange
aux = l[i]
l[i] = l[ind]
l[ind] = aux

Complexitate timp de
execuie

Numrul total de comparaii este:


n
n1
n (n 1)
i1 j i1
2
1 (n )
2

Este independent de datele de intrare:


caz favorabil/defavorabil/mediu sunt la
fel, complexitatea este
(n2 ) .

Complexitate spaiu de memorie


(1)
Sortare prin selecie este un algoritm In-place:
memoria adiional (alta dect memoria necesar
pentru datele de intrare) este
Selecti
on
In-place . Algoritmul care nu folote memorie adiional
sort is
(doar un mic factor constant). an in-
Out-of-place sau not-in-space. Algoritmul folosete place
memorie adiional pentru sortare. sorting
algorit
hm.

Sortare prin selecie direct (Direct selection sort)


def directSelectionSort(l):
"""
sort the element of the
list l - list of element
return the ordered list (l[0]<l[1]<...)
"""
for i in range(0,len(l)-1):
#select the smallest element
for j in range(i+1,len(l)):
if l[j]<l[i]:
swap(l,i,j)

n (n 1)
Overall time complexity: n1 n 1 (n 2 )
i1 j i1 2

Sortare prin inserie - Insertion Sort


se parcurg elementele

se insereaz elementul curent pe poziia corect n


sub- secvena deja sortat.

n sub-secvena ce conine elementele deja sortate


se in elementele sortate pe tot parcursul
algoritmului, astfel dup ce parcurgem toate
elementele secvena este sortat n ntregime

Sortare prin inserie


def insertSort(l):
"""
sort the element of the
list l - list of element
return the ordered list (l[0]<l[1]<...)
"""
for i in range(1,len(l)):
ind = i-1
a = l[i]
#insert a in the right position
while ind>=0 and a<l[ind]:
l[ind+1] = l[ind]
ind = ind-1
l[ind+1] = a

Insertion Sort - complexitate timp de execuie

Caz defavorabil:

T (n)
i 2
n (n 1)
(n ) 2
2
(i 1)
nu rul maxim de iteraii cnd lista este ordonat
Avem m descresctor

Caz mediu:
2
n 3 n n


1
(n )
2

4 i 1 i

Pentru un i fixat i un k,
1ki, probabilitatea ca xi
i
s fie al k-lea cel mai mare
element n
x1 , x 2 ,..., xi
subsecvena
este 1
un i i 2 i
Astfel, pentru i fixat, i
putem deduce: fix
(i
at
est 1
Numarul de )
e:
iteraii while numrul2de iteraii
c
1 i

1
1
2 1

1
2
... 1
i
... i
i-1
i
(i
Rezult c numrul de 2)
iteraii while medii pentru

Caz favorabil:
T (n) 1 n 1 (n)
i 2

lista este sortat

Sortare prin inserie

complexitate general este


2
O(n ) .

Complexitate spaiu de meorie

complexitate memorie aditional este:

(1) .
Sortare prin inserie este un
algoritm in-place.

Sortare prin selecie direct


def directSelectionSort(l):
"""
sort the element of the
list l - list of element
return the ordered list (l[0]<l[1]<...)
"""
for i in range(0,len(l)-1):
#select the smallest element
for j in range(i+1,len(l)):
if l[j]<l[i]:
swap(l,i,j)

n (n 1)
Overall time complexity: n1 n 1 (n 2 )
i1 j i1 2

Sortare prin inserie - Insertion Sort

se parcurg elementele

se insereaz elementul curent pe poziia corect n


sub- secvena deja sortat.

n sub-secvena ce conine elementele deja sortate


se in elementele sortate pe tot parcursul
algoritmului, astfel dup ce parcurgem toate
elementele secvena este sortat n ntregime

Sortare prin inserie


def insertSort(l):
"""
sort the element of the
list l - list of element
return the ordered list (l[0]<l[1]<...)
"""
for i in range(1,len(l)):
ind = i-1
a = l[i]
#insert a in the right position
while ind>=0 and a<l[ind]:
l[ind+1] = l[ind]
ind = ind-1
l[ind+1] = a

Metoda bulelor - Bubble sort

Compar elemente consecutive, dac nu sunt n ordinea dorit, se


interschib.

Procesul de comparare continu pn cnd nu mai avem elemente


consecutive ce trebuie interschimbate (toate perechile respect
relaia de ordine dat).

Sortare prin metoda bulelor


def bubbleSort(l):
sorted = False
while not sorted:
sorted = True #assume the list is already sorted
for i in range(1,len(l)):
if l[i-1]>l[i]:
swap(l, i, i-1)
sorted = False #the list is not sorted yet
Complexitate metoda bulelor

Caz favorabil: (n) . Lista este sortat

Caz defavorabil: (n 2 ) . Lista este sortat descresctor

Caz mediu
(n 2 ) .

Coplexitate general este


2
O(n )

Complexitate ca spaiu adiional de memorie este


(1) .

este un algoritm
de sortare in-place .

QuickSort

Bazat pe divide and conquer


Divide: se partiioneaz lista n 2 astfel nct elementele
din dreapta pivotului sunt mai mici dect elementele
din stnga pivotului.
Conquer: se sorteaz cele dou subliste
Combine: trivial dac partitionarea se face n acelai list
Partiionare: re-aranjarea elementelor astfel nct elementul numit
pivot ocup locul final n secven. Dac poziia pivotului este i :
kj ki kl , for Left j < i < l Right

Quick-Sort
def partition(l,left,right):
"""
Split the values:
smaller pivot greater return pivot position
post: left we have < pivot right we have > pivot
"""
pivot = l[left] i = left
j = right while i!=j:
while l[j]>=pivot and i<j: j = j-1
l[i] = l[j]
while l[i]<=pivot and i<j: i = i+1
l[j] = l[i] l[i] = pivot return i

def quickSortRec(l,left,right):

#partition the list


pos = partition(l, left, right)

#order the left part if left<pos-1:


quickSortRec(l, left, pos-1)
#order the right part if pos+1<right:
quickSortRec(l, pos+1, right)
mai mari)
QuickSort
complexitate timp
de execuie Partiionarea necesit timp
linear.
Timpul de execuie
depinde de distribuia Caz favorabil:, partiioanarea
partiionrii (cte exact la mijloc (numere mai
elemente sunt mai mici mici ca pivotul = cu numere
dect pivotul cte sunt mai mari ca pivotul):
n
T (n) 2 T (n)

2 (n log 2 n) .

Complexitatea este
partiie cu n-1 elemente
QuickSort T (n) T (1) T (n 1) (n) T (n
Caz favorabil 1) (n) (k) (n ) .
2

k 1

caz defavorabil: dac elementele sunt n ordine


QuickSort invers
Caz
defavorabil QuickSort Caz mediu
Se alterneaz cazurile:
Partiionarea tot caz favorabil (lucky) cu complecitatea
timpul rezult ntr-
o partiie cu un n

singur element i o
(n log 2 n) (notm cu L )
caz defavorabil (unlucky) cu (n 2 ) (notm cu U ).
complexitatea

Avem recurena:

n
L lucky case
)
U

(n
2
U (n) L(n 1) (n)

unlucky case

Rezult

n
L(n
L ( log
1 L

Complexitatea caz mediu:


T (n) L(n) (n log 2 n) .

Coplexitatea ca
timp de
execuie pentru
sortri: Algorithm worst-c
SelectionSort (n 2
rezult din operaii asupra unor elemente dintr-o
alt secven
paranteze drepte coninnd o expresie urmat de o
clauz for , apoi zero sau mai multe clauze for sau
if
Python -
Python Parametrii optionali parametrii cu
nume
Quick-Sort
def qsort(list):
def f(a=7,b = [],c="adsdsa"):
"""
Quicksort using list comprehensions

"""
if list == []:
return []
else:
pivot = list[0]
lesser = qsort([x for x in list[1:] if x < pivot])
greater = qsort([x for x in list[1:] if x >= pivot])
return lesser + [pivot] + greater
Putem avea parametrii cu valori default;

List Dac se apeleaz metoda fr parametru actual se vor folosi


valorile default
comprehension
[x for x in list[1:] if x < pivot]
def f(a=7,b = [],c="adsdsa"): Console:
s generatoare print (a)
print (b)
7
[]
print (c) adsdsa
de liste f()

rez = []
for x in l[1:]:
if x<pivot:
rez.append(x)

Argumentele se pot specifica n orice ordine


Variant concis de f(b=[1,2],c="abc",a=20) Console:
20
a crea liste [1, 2]
abc
creaz liste unde
elementele listei Parametrii formali se adaug ntr-un dicionar (namespace)
Trebuie oferit o poziionali
valoare actual d
pentru fiecare e
parametru formal - f
prin orice metod:
standard, prin nume, f
default value u
n
c
Tipuri de parametrii
(
cum specificm *
parametru actual a
r
g
positional-or-keyword : s
parametru poate fi )
transmis prin poziie sau :
prin nume (keyword) Valorile transmise se pot accesa folosind args, args este un
def tuplu
func(foo
,
bar=None var-keyword: un numr arbitrar de parametrii prin nume
): def func(**args):

keyword-only : Valorile transmise se pot accesa folosind args, args este un


parametru poate fi dictionar
transmis doar specificnd
numele Sortare n python - list.sort() / funcie build
def in : sorted
func(arg sort(*, key=None,reverse=None)
, *, Sorteaz folosind operatorul <. Lista curent este sortat
kw_only1 (nu se creaz o alt list)
,
kw_only2 key o funcie cu un argument care calculeaza o valoare
): pentru fiecare element, ordonarea se face dupa valoarea
Tot ce apare dupa * se cheii. n loc de o1 < o2 se face key(o1) < key(o2)
poate transmite doar prin reverse true daca vrem sa sortm descresctor
nume l.sort() l.sort(reverse=T
print (l) print (l)

var-positional : se pot sorted(iterable[, key][, reverse])


transmite un numr
Returneaz lista sortat
arbitrar de parametri
l = sorted([1,7,3,2,5,4]) lambda pot referi variabile din namespace
print (l) l = []
l = [] l.append(MyC
l = sorted([1,7,3,2,5,4],reverse=
l.append(MyClass(2,"a")) l.append(MyC
print (l) l.append(MyClass(7,"d")) l.append(MyC
l.append(MyClass(1,"c")) l.append(MyC
l.append(MyClass(6,"b")) #sort on id
#sort on name ls = sorted(
ls = sorted(l,key=lambda o:o.name) for x in ls:
for x in ls: print (x
Sort stability print (x)

Stabil (stable) dac avem


mai multe elemente cu aceli TreeSort
cheie, se menine ordinea
iniial
Algoritmul creaz un arbore binar cu
Python
proprietatea c la orice nod din arbore, arborele
funcii stng conine doar elemente mai mici dect
lambd
a elementul din rdcin iar arborele drept
(anony
conine doar elemente mai mari dect rdcina.
mous
functio
ns,
lambd
Dac parcurgem arborele putem lua elementele
a n ordine cresctoare/descresctoare.
form)
Folosind lambda
putem crea mici funcii Arborele este construit incremental prin
inserarea succesiv de elemente. Elementele se
Funciile lambda pot fi
folosite oriunde e nevoie insereaz astfel nct se menine proprietatea ca
de un obiect funcie n stnga avem doar elemente mai mici n
def f(x):
return x+7 dreapta doar elemente mai mari dect elementul
print ( f(5) )
din rdcin.
Putem avea doar o
expresie.
Sunt o metod Elementul nou inserat tot timpul ajunge ntr-un
convenient de a crea nod terminal (frunz) n arbore.
funcii mici.
Similar cu
funciile MergeSort
definite n
interiorul
altor funcii,
funciile
Bazat pe divide and conquer.
n, (yi, i=1,n);
Secvena este
nprit n dou Precondiii: {x1 <=
subsecvene i
fiecare subsecvena x2...<=xm} i {y1 <= y2
este sortat. Dupa
sortare se <=... <=yn} Rezultate k,
interclaseaz cele
dou subsecvene, (z , i=1,k);
i
astfel rezult
secvena sortat n Post-condiii: {k=m+n} i {z1<=z2<=...<=zk}
ntregime. i (z1,z2,..., zk) este o permutare a
valorilor (x1, ..., xm,y1,..., yn)
Pentru subsecvene
se aplic acelai
abordare pn cnd complexitate interclasare: (m n) .
ajungem la o Spaiu de memorare adiional pentru merge sort
subsecven
elementar care se
poate sorta fr
nprire (secven
cu un singur
element).

Interclasare
(Merging)

Date m, (xi, i=1,m),


(1)

technic
Technici de programare
Divide and conquer
Metoda divizrii -
strategii de rezolvare a pai
problemelor mai dificile
algoritmi generali Pas 1 Divide -
pentru
rezolvarea se mparte
unor problema n
tipuri de probleme mai
probleme mici (de acelai
de multe ori o problem structur)
mprirea problemei n
se poate rezolva cu mai dou sau mai multe
multe technici se alege probleme disjuncte care
se poate rezolva
metoda mai eficient folosind acelai algoritm

problema trebuie s Pas 2 Conquer


satisfac anumite se rezolv
subproblemele
criterii pentru a putea recursiv
aplica technica Step3 Combine
descriem algoritmul combinarea
general pentru fiecare rezultatelor
Divide and conquer if n is small enough

algoritm general
def divideAndConquer(data):
if size(data)<a:
#solve the problem directly
#base case
return rez
#decompose data into d1,d2,..,dk
rez_1 = divideAndConquer(d1)
rez_2 = divideAndConquer(d2)
...
rez_k = divideAndConquer(dk)
#combine the results
return combine(rez_1,rez_2,...,rez_k)

Putem aplica divide and


conquer dac:
O promblem P pe un set de date D poate fi
rezolvat prin rezolvarea aceleiai probleme P pe
un alt set de date D= d1, d2, ..., dk, de dimensiune
mai mic dect dimensiunea lui D

Complexitatea ca timp de execuie pentru o problem


rezolvat folosind divide and conquer poate fi descris
de recurena:

solving trivial problem,


T (n)
k T (n / k ) for dividing time
time
for combining, otherwise
d iune n-1 Exemplu: Caut
Divide and e maximul
conquer Complexitate timp
d
1 / n-1 i
P m
u e
t n
e s
m i
u
d n
i e
v
i 1
d
e
i
d
a d
t a
e t
l e
e
d
e
n
: d
i
d m
a e
t n
e s
T n Recurena:
1 for n1
T n11 otherwise

T n
T n11
T n1 T
n21 T => T n11...1n n

n2 T
n31
... ...
T 2 T 1
2 k 2 3 k 3 2
2 T2 2 T 2 2 =>
Divizare n date de ... ...

def findMax(l):
"""
find the greatest element in the
list l list of elements
return
max """
if len(l)==1:
#base case
return l[0]
#divide into 2 of size n/2
mid = len(l) /2
max1 = findMax(l[:mid])
max2 = findMax(l[mid:])
#combine the results
if max1<max2:
return max2
return max1

2k1T 2 2k T 12
dimensiune n/k
Complexitate ca timp:
Recurena:T
1

n
2
otherwise

k
2 T 2k11 2 T 2k1 22 T 2k22
Notm: n2k
=> k log2 n
T n12k1 1
22. ..2k2k11212k 212 n1n

Divide and conquer - Exemplu


Calculai xk unde k 1 numr ntreg
Aborare simpl: xk kk...k - k-1 nmuliri (se poate folosi un for) T n n


Rezolvare cu metoda divizrii:
k
x x
k2

xk
2

fo
r
k
ev
en

x
k
2

x
k
2
x
f
o
r
k
o
d
d

def power(x, k): Divide: calculeaz k/2


"""
compute x^k Conquer: un apel recursiv pentru a calcu
x real number
k integer number
return x^k
Combine: una sau doua nmuliri
""" Complexitate: T nlog2 n
if k==1:
#base case
return x
#divide
half = k/2
aux = power(x, half)
#conquer if
k%2==0:
return aux*aux
else:
return aux*aux*x

Divide and conquer


Cutare binar ( T nlog2 n )
Divide imprim lista n dou liste egale
Conquer cutm n stnga sau n dreapta
Combine nu e nevoie
Quick-Sort ( T n n log2 n mediu)
Merge-Sort
Divide imprim lista n dou liste egale
Conquer sortare recursiv pentru cele dou liste
Combine interclasare liste sortate

Backtracking
se aplic la probleme de cutare unde se caut mai multe soluii
genereaz toate soluiile (dac sunt mai multe) pentru problem
caut sistematic prin toate variantele de soluii posibile
este o metod sistematic de a itera toate posibilele configuraii n spaiu de
cutare
este o technic general trebuie adaptat pentru fiecare problem n
parte.
Dezavantaj are timp de execuie exponenial

Algoritm general de descoperire a tuturor soluiilor unei


probleme de calcul Se bazeaz pe construirea incremental de
soluii-candidat, abandonnd fiecare candidat parial imediat ce
devine clar c acesta nu are anse s devin o soluie valid

Metoda generrii i testrii (Generate and test)


Problem Fie n un numr natural. Tiprii toate permutrile numerelor
1, 2, ..., n. Pentru n=3
def perm3(): [0, 1, 2]
for i in range(0,3): [0, 2, 1]
for j in range(0,3): [1, 0, 2]
for k in range(0,3): [1, 2, 0]
#a possible solution [2, 0, 1]
possibleSol = [i,j,k] [2, 1, 0]
if i!=j and j!=k and i!=k:
#is a solution
print possibleSol

Metoda generrii i testrii - Generate and Test


Generare: se genereaz toate variantele posibile de liste de lungime 3
care conin doar numerele 0,1,2
Testare: se testeaz fiecare variant pentru a verifica dac este soluie.

Generare i testare toate combinaiile posibile


x 1 2 3
1

x 1 2 2 3 1 2 3 1 2 3

1 2 3 1 2 3 1 x2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1
3

Probleme:

Numrul total de liste generate este 33, n cazul general nn

iniial se genereaz toate componentele listei, apoi se verifica dac lista este o
permutare in unele cazul nu era nevoie sa continum generarea (ex. Lista ce
incepe cu 1,1 sigur nu conduce la o permutare

Nu este general. Funcioneaz doar pentru n=3

n general: dac n este afncimea arborelui (numrul de variabile/componente n soluie) i


presupunnd c fiecare
component poate avea k posibile valori, numrul de noduri n arbore este arborele avem o
n
complexitate exponenial, O(k ) .
n
k . nseamn c pentru cutarea n ntreg

nbuntiri posibile

s evitm crearea comlet a soluiei


posibile n cazul n care tim cu siguran
c nu se ajunge la o soluie.
Dac prima component este 1, atunci nu are
sens s asignam 1 s pentru a doua component
1 2 x 3
1

1 2 3 1 2
x
3 2 1 2 3

1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 x 1 2 3 1 2 3 1
3

lucrm cu liste pariale


(soluie parial)
extindem lista cu
componente noi doar
dac sunt ndeplinite
anumite condiii
(condiii de
continuare)
dac lista parial
nu conine duplicate

Generate and test


- recursiv
folosim recursivitate pentru
a genera toate soluiile
posibile (soluii candidat)
def generate(x,DIM):
if len(x)==DIM:
print x
if len(x)>DIM:
return
x.append(0)
for i in range(0,DIM):
x[-1] = i
generate(x[:],DIM)

generate([],3)

Testare se
tiprete doar
soluia
def generateAndTest(x,DIM): [0, 1, 2] Rezultat: 8 poziii de
if len(x)==DIM and isSet(x): [0, 2, regine
1] pe tabl
print x [1, 0, 2]
if len(x)>DIM: [1, 2, 0] Un rezultat partial e valid:
return [2, 0, 1]
x.append(0) [2,
dac nu exist regine care se
1, 0]
for i in range(0,DIM): atac
x[-1] = i
generateAndTest(x[:],DIM) nu e pe aceli coloana,
generateAndTest([],3) linieor sau diagonal
Numrul total de posibile
n continuare se genereaza toate listele ex: poziii (att valide ct i
invalide):
liste care ncep cu 0,0
combinri de 64
ar trebui sa nu mai generm dac conine luate cte 8, C(64,
duplicate Ex (0,0) aceste liste cu siguran 8) 4.5 109)
nu conduc la rezultat la o permutare Genereaz i testeaz nu
rezolv problma n timp
rezonabil
Reducem spatiu de cutare nu generm chiar
toate listele posibile

Un candidat e valid (merit s continum cu el) Ar trebui sa generm doar


doar dac nu conine duplicate poziii care pot conduce la
def backtracking(x,DIM):
un rezultat (sa reducem
if len(x)==DIM: spaiu de cutare)
print x
if len(x)>DIM: Dac avem
return #stop recursion
x.append(0) deja 2 regine
for i in range(0,DIM):
x[-1] = i care se atac
if isSet(x):
#continue only if x can conduct to a solution nu ar trebui s
backtracking(x[:],DIM)
mai continum
backtracking([], 3)
cu aceast
Este mai bine dect varianta genereaz i testeaz, configuraie
avem nevoie de
dar complexitatea ca timp de execuie este tot
toate soluiile
exponenial.
Backtracking
Permutation problem spaiu de cutare: S = S1 x S2 x
x x0, x1,. .. , xn , xi 0,1,. . , n1 ... x Sn;
rezultat:
x este un vector ce reprezint
e o solutie: xi x j for any i j soluia;
x[1..k] n S1 x S2 x ... x Sk este o
8 Queens problem: soluie candidat; este o configuraie
parial care ar putea conduce la
Plasai pe o tabl de sah 8 regine care nu se atac.
rezultat; k
este numrul de componente deja construit;
consistent o funcie care verific dac o soluie parial Metoda Greedy
este soluie candidat (poate conduce la rezultat)
soluie este o funcie care verific dac o soluie candidat
x[1..k] este o soluie pentru problem.

def backRec(x):
x.append(0) #add a new component to the candidate solution
for i in range(0,DIM):
x[-1] = i #set current component
if consistent(x):
if solution(x):
solutionFound(x)
backRec(x[:]) #recursive invocation to deal with next components

Algoritmul Backtracking recursiv

Algoritm mai general (componentele soluiei pot o strategie de


avea domenii diferite (iau valori din domenii rezolvare pentru
diferite)
probleme de
optimizare
Backtracking
aplicabil unde
Cum rezolvm problema folosind algoritmul
generic: optimul global se
trebuie sa reprezentm soluia sub forma
poate afla
unui vector X x0, x1, ... xn S 0 x S 1 x ... x S n selectnd succesiv
definim ce este o soluie candidat valid optime locale
(condiie prin care reducem spaiu de cutare)
permite rezolvarea
definim condiia care ne zice daca o soluie
candidat este soluie
problemei fara a
reveni asupra
alegerilor facute
def consistent(x):
"""
The candidate can lead to an actual
permutation only if there are no duplicate
elements """
return isSet(x)

def solution(x):
"""
The candidate x is a solution if
we have all the elements in the
permutation """
return len(x)==DIM
pe parcurs
folosit n multe probleme practice
care necesite selecia unei mulimi Avnd un set de
obiecte C candidai
de elemente care satisfac anumite
pentru a face parte din
condiii i realizeaz un optim
soluie, se cere s se
Probleme gseasc un subset B (B
C) care indeplinete
Problema rucsacului anumite condiii (condiii
interne ) i maximizeaz
Se d un set de obiecte, caracterizate prin (minimizeaz) o funcie
greutate i utilitate, i un rucsac care are de obiectiv.
capacitatea totala
W. Se caut o submultime de obiecte astfel
nct greutatea total este mai mic dect W i Dac un subset X
suma utilitii obiectelor este maximal. ndeplinete
condiiile interne
atunci subsetul X este
Monede acceptabil (posibil)
Unele probleme pot

Se da o sum M i tipuri (ex: 1, 5, avea mai multe


25) de monede (avem un numar nelimitat soluii acceptabile,
caz n care se caut
de monede din fiecare tip de moned). S
o soluie ct mai
se gseasc o modalitate de a plti suma
bun, daca se poate
M de bani folosind ct mai puine solutia ceea mai
monezi. bun (cel care
realizeaza maximul
Forma general a unei pentru o funcie
probleme Greedy-like obiectiv).
Pentru a putea rezolva o problema folosind n unele situaii se
metoda Greedy, problema trebuie s satisfac prefer o soluie,
proprietatea: chiar i
dac B este o soluie acceptabil i XB suboptim, dar
atunci i X este o soluie acceptabil obinut n timp
rezonabil
Algoritmul Greedy
Construiete treptat
soluia (fr reveniri
Algoritmul Greedy gsete soluia ca n cazul
backtracking)
incremetal, construind soluii acceptabile
Furnizeaz o singur
care se tot extind pe parcurs. La fiecare soluie
pas soluia este exstins cu cel mai bun
Timp de lucru
candidat dintre candidaii rmai la un polinomial
moment dat. (Strategie greedy - lacom) Greedy python

def greedy(c):
Principiu (strategia) Greedy : """
adaug succesiv la rezultat elementul Greedy
care realizeaz optimul local algorithm
c - a list of
o decizie luat pe parcurs nu se mai candidates
return a
modific ulterior list (B) the
Algoritmul Greedy solution found (if
exists) using the
greedy strategy,
Poate furniza soluia optim (doar pentru None if the
anumite probleme) algorithm
selectMostP
alegerea optimului local nu garanteaza romissing - a
tot timpul optimul global function that
return the most
solutie optim - dac se gsete o promising
modalitate de a alege (optimul candidate
local) astfel ncat se ajunge la acceptable -
a function that
solutie optim
returns True if a candidate t
solution can be extended to a h
solution e
solution - verify if a given
candidate is a solution b
""" e
b = [] s
#start t
with an
empty set c
as a a
candidate n
solution d
while not i
solution( d
b) and c! a
=[]: t
# e
s )
e
l c
e a
c n
t d
i
t d
h a
e t
e
l
o =
c
a s
l e
l
o e
p c
t t
i M
m o
u s
m t
P
( r
o e
m m
i o
s v
s e
i (
n c
g a
( n
c d
) i
# d
r a
e t
m e
o )
v #
e i
f
t
h t
e h
e
c
u n
r e
r w
e
n e
t x
t
c e
a n
n d
d e
i d
d
a c
t a
e n
d
c i
. d
r a
te o
solution
is s
acceptab o
le if l
acceptab u
le(b+ t
[candida i
te]): o
b.append(candidate) n

if r
s e
o t
l u
u r
t n
i
o N
n o
( n
b e
)
: Algoritm Greedy
r
- elemente
e
t
u 1. Mulimea
r candidat
n (candidate set)
de unde se aleg
b elementele soluiei
#
t 2. Funcie de
h selecie
e
r (selection
e function)
i alege cel
s mai bun
n
candidat
pentru a fi adugat la soluie; d
3. Acceptabil (feasibility function) e
folosit pentru a determina dac un
candidat poate contribui la soluie -
4. Funcie obiectiv ( objective function)
o valoare pentru soluie i pentru C
orice soluie parial O
5. Soluie (solution function), - indic I
dac am ajuns la soluie N
S
Exemplu
=
Se da o sum M i tipuri (ex: 1, 5,
25) de monede (avem un numar nelimitat {
1
de monede din fiecare tip de moned). S ,
se gseasc o modalitate de a plti suma
M de bani folosind ct mai puine 5
monezi. ,
Set Candidat: 2
L 5
i ,
s
t 5
a 0
}
d
e S
o
m l
o u
n
e
i candidat este egal
e cu suma cerut
Monede cod
C
a
python
n
d #Let us consider that we have a sum M
i #The problem is to establish a modalit
def selectMostPromissing(c):
d """
select the largest coin from the
a c - candidate coins
t return a coin
"""
: return max(c)
def solution(b):
o list de monede - X X 0, X 1,. . , X k
"""
unde verify if a candidate solution is a
basically verify if the coins condu
X i COINS b candidate solution
"""
moned sum = _computeSum(b)
return sum==SUM

def _computeSum(b):
Funcia de selecie: """
candidate solution: X X X . , X
0, 1,. k
compute the payed amount with the c
return int, the payment
alege moneda cea mai mare care e b candidate solution
"""
mai mic dect ce mai e de pltit sum = 0
for coin in b:
din sum nrCoins = (SUM-sum) / coin
#if this is in a candidate sol
use at least 1 coin
if nrCoins==0: nrCoins =1
Acceptabil (feasibility function):
k sum += nrCoins*coin
Soluie candidat: X X X . , X S
0, 1,. k
return sum

X M
i0 i
Greedy
suma monedelor din soluia
candidat nu depete suma cerut
1. Algoritmul Greedy are
Soluie: k
complexitate
Soluie candidat: X X X . , X
0, 1,. k S polinomial - candidat
X M
i0 i C
suma monedelor din soluia
2
O(n )
unde n este numrul de
elemente din lista
2. nainte de a aplica Greedy este n probleme pentru care
nevoie s demonstrm c metoda e Greedy nu gsete soluia
gsete soluia optim. De multe o optim. n unele cazuri se
ori demonstraia este netrivial ri prefer o soluie obinut n
3. Exist o mulime de probleme ce e timp rezonabil
se pot rezolva cu greedy. Ex: nt (polinomial) care e aprope
Algoritmul Kruskal at de soluia optim, n loc de
determinarea arborelui de4. Ex soluia optim obinut n
acoperire, Dijkstra, Bellman- is timp exponenial
Kalaba drum minim ntrun graf t (heuristics algorithms).
Programare Dinamic

Se poate folosi pentru a rezolva probleme de optimizare, unde:


soluia este rezultatul unui ir de decizii, d1, d2, ..., dn,

principiul optimalitii este satisfcut.

n general timp polinomial de execuie


tot timpul gsete soluia optim.
Rezolv problema combinn sub soluii de la subprobleme,
calculeaz subsoluia doar o singur data (salvnd rezultatul pentru a
fi refolosit mai trziu).

Fie stritle s0, s1, ..., sn-1, sn, unde s0 este starea iniial, sn este starea
final, prin aplicariea succesiv a deciziilor d1, d2, ..., dn se ajunge la starea
final (decizia di duce din starea si-1 n starea si, pentru i=1,n):

d1 d2
s0 s1 s2
Programare Dinamic

Caracteristici:
principiul optimalitii;
probleme suprapuse (overlapping sub problems);
memoization.

Principiul optimalitii
optimul general implic optimul parial
la greedy aveam optimul local implic optimul global

ntr-o secven de decizii optime, fiecare decizie este optim.


Principiul nu este satisfcut pentru orice fel de problem. In general
nu e adevrat n cazul in care subsecvenele de decizii nu sunt
independente i optimizarea uneia este n conflict cu optimizarea de
la alta secvena de decizii.

Principiul optimalitii
Dac
d1 , d 2 ,..., d n

este o secven de decizii care conduce optim sistemul din starea


iniial
s0 n final atunci una din utmtoarele sunt satisfcute:
starea sn ,
1).
d k , d k 1 ,..., d n
este o secven optim de decizii care conduce sistemul din starea
sk 1 n
st sn , k, 1 k n . ( forward variant decizia dk
a depinde de deciziile dk+1..dn)
r
e
a
2). d este o secven optim de decizii care conduce sistemul
1
, din starea
d

2
,
.
.
.
,
d

k
s0 n

st sk , k, 1 k n . (backward variant)
a
r
e
a
3). d i d1 , d 2 ,..., d k
k

1
,
d
k

2
,.
..
,
d
n
sunt s ecvene optime de decizii care conduc sistemul din
st s n , n starea
k
a starea respecti
r s v, din
n
e starea
a s0
sk , k, 1 k n . (mixed variant)
suprapuse problema are sub- mai o (Memorization)
(Overlapp probleme multe ri salvarea rezultatelor de
ing Sub- suprapuse daca ori z la o subproblem
Sub-
problems) poate fi inprit n a pentru a fi refolosit
Proble
O subprobleme care r
me Mem e
se refolosesc de
Cum aplicm programare dinamic

Principiul optimalitii (oricare variant: forward, backward or


mixed) este demonstrat.
Se definete structura solutiei optime.
Bazat pe principiul optimalitii, valoarea soluiei optime se
definete recursiv. Se definete o recuren care indic
modalitatea prin care se opine optimul general din optime
pariale.
Soluia optim

se calculeaz
in manier bottom-up, ncepem cu subproblema cea mai
simpl p entru care soluia este cunoscut.
Cea mai lung sublist cresctoare

Se d o list
Soluie:
a1 , a2 ,..., an . Determinai cea mai lung sublist cresctoare
a , a ,..., a i
i1 2 s

a listei date.
Pricip alitii T
iul varianta nainte forward h structu
optim e re of
the optimal solution
Construim dou iruri:

l l1 .l2 ,...ln i

p p1. p2 ,... pn .

lk ak .
lungime
sublistei
care
incepe
cu
elementu
l
p

k
indexul elementului a care dup
urmeaz
element n sublista cea mai lung care
ul ak
in pe cu ak . D fi ia recursiv
ce e ni ln 1, pn 0

lk
max{1 li | ai ak , k 1 i n},
k n 1, n 2,...1

pk
arg max{1 li | ai ak , k n 1, n 2,...,1
k 1 i n},

Ce
a
ma
i
lun
g
su
bli
st
cre
sc
toa
re
pyt
ho
n
def longestSublist(a):
"""
Determines the longest
increasing sub-list a - a list
of element
return sublist of x, the longest increasing sublist

"""
#initial
ise l
and p l
=
[0]*len(
a)
p = [0]*len(a)
l[lg-1] = 1
p[lg-1]=-1
for k in range(lg-
2, -1, -1):
print p, l
p[k] = -1
l[k]=1
for i in range(k+1, lg):
if a[i]>=a[k] and
l[k]<l[i]+1:
l[k] = l[i]+1
p[k] = i

#identify the longest sublist


#find the
maximum
length j = 0
for i in
range(0