Sunteți pe pagina 1din 15

Cum se programeaz

-- un set de reguli pentru scrierea eficient de programe -Mihai BUDIU - mihaib@pub.ro


februarie 1996
Subiect:
reguli pentru scrierea programelor.
Cuvinte cheie:
comentariu, indentare, modul, variabil, procedur.
Cunotine necesare:
cel puin un limbaj de programare.
Contents

Introducere

Regula de aur

mprirea programului

Minima vizibilitate

Spaiile albe; i(n)dentarea

Comentariile

Variabilele, constantele, funciile, procedurile

Fiabilitatea

Documentaia

Portabilitatea

Introducere
Voi ncepe cu un citat:
As soon as we started programming, we found to our surprise that it wasn't as easy to get
programs right as we had thought. Debugging had to be discovered. I can remember the
exact instant when I realized that a large part of my life from then on was going to be
spent in finding mistakes in my own programs.
-- Maurice Wilkes discovers debugging, 1949
n traducere asta ar suna cam aa:
Imediat ce am nceput s programm am descoperit spre surprinderea noastr ca nu era
aa de uor s facem programe bune pe ct crezusem. Depanarea (debugging) trebuia s
1

fie inventat. mi pot aminti cu precizie momentul exact cnd am realizat c o mare parte
din viaa mea de atunci ncolo urma s fie petrecut n a gsi greeli n propriile mele
programe.
De asemenea -- pentru contrast -- mi place s citez urmtorul fragment din manualul care
nsoete programul MetaFont, scris de marele informatician Donald Knuth:
On January 4, 1986 the ``final'' bug in Metafont was discovered and removed. If an error
still lurks in the code, Donald E. Knuth promises to pay a finder's fee which doubles
every year to the first person who finds it. Happy hunting.
Adic:
Pe 4 ianuarie 1986 ``ultimul'' bug (defect) din Metafont a fost descoperit i scos. Dac o
eroare mai zace prin cod, Donald E. Knuth promite s plteasc o sum care se dubleaz
n fiecare an celui care o gsete. Vntoare plcut.
Ci dintre dumneavoastr au reuit s scrie un program mai lung de 2000 de linii surs
(ntr-un limbaj de nivel nalt) care s exhibe acelai ngrijortor simptom al perfeciunii?
Cu riscul de a v agasa voi mai cita un monstru sacru al informaticii, de data asta ns din
memorie. Edsger Dijkstra spunea n cuvntarea sa fcut cu prilejul decernrii premiului
``Turing'' al prestigioasei societi americane ACM (Association for Computing
Machinery), cuvntare care dealtfel se numea ``The Humble Programmer'' (Programatorul
Umil), c un bun programator trebuie s recunoasc faptul c sarcina ce i-a asumat-o l
depete cu mult datorit complexitii sale, i ca atare trebuie s ncerce ct mai mult s
o reduc la buci mai mici, ct mai uor de manevrat.
Dei aparent legturile dintre cele trei citate de mai sus sunt cam ubrede, sper c textul
care urmeaz va arta c de fapt acestea sunt trei fee ale aceluiai poliedru.
n rezolvarea unei probleme cu calculatorul se pot distinge mai multe etape (chiar dac
ele nu sunt distincte temporal): analiza problemei, elaborarea algoritmului,
implementarea, documentarea, testarea, etc. Acest articol se ocup de una singur dintre
ele, care dealtfel este foarte puin dependent de problema care trebuie rezolvat:
presupun c tiu algoritmii, tot ce trebuie s fac este s-i implementez ntr-un limbaj de
programare oarecare. Aparent banal, etapa aceasta se dovedete adesea foarte intricat.
De fapt acest lucru l arat i primul citat din introducere.
Cele mai multe consideraii care urmeaz sunt independente de limbajul ales.
Regula de aur
Un program trebuie s fie scris n aa fel nct s poat fi citit de ctre oricine.
Programele sunt fcute n aa fel nct prile sunt strns dependente. Trebuie ca cele
scrise ntr-un loc s fie folosite ntr-altul (i eventual refolosite). Mintea unui om normal
nu poate reine toate amnuntele din textul unui program, de aceea trebuie s-i dm un
ajutor.

Sfatul acesta este clar obligatoriu cnd se dezvolt soft n echip, pentru c ceea ce scrie
unul trebuie s fie folosit de altul. Paradoxal este c el este extrem de oportun chiar cnd
ntreg procesul de scriere este fcut de un singur programator. Nu vi s-a ntmplat
niciodat s v uitai la o funcie scris n urm cu o lun i s v mirai c ai putut scrie
aa ceva? Sau s v ntrebai ce face? Acel ``oricine'' din sfat poate fi deci un ``alter ego''
al dumneavoastr.
Aparent scrierea ngrijit a unui program consum prea mult timp.
Pentru programe scurta acest lucru poate fi adevrat. Pentru programe la care lucrai mai
mult de o sptmn timpul cheltuit cu o scriere ngrijit este recuperat nzecit n fazele
ulterioare ale dezvoltrii (depanare, extindere, ntreinere). Dac nu credei, ncercai
mcar o dat.
mprirea programului
F bucile n aa fel nct s nelegi uor ce face fiecare fr a trebui s tii cum face.
Domnul Dijkstra (n olandez se citete aproximativ ``daicstra'') recomanda o tactic
veche de pe vremea lui Cezar: ``divide et impera'': ``mparte i stpnete''. Aa e bine s
scriei i programele: n buci ct mai independente unele de altele, care interacioneaz
intre ele n moduri ct se poate de clare i simple, i care nu se influeneaz ``pe la spate''.
Din fericire toate limbajele moderne de programare aduc un suport extrem de eficace
acestei metode de scriere: funciile, procedurile i modulele.
mprii deci programul dumneavoastr n astfel de pri ct mai des cu putin. Facei
prile logic independente; funciunea fiecreia trebuie s fie extrem de clar i ct se
poate de limpede enunat. De exemplu, o funcie care caut ceva ntr-un ir nu trebuie s
fac i ordonarea irului. Facei dou funcii independente pentru asta, una de cutare i
una de sortare.
Dac limbajul permite module, atunci facei-le n fiiere separate, ale cror nume s fie
suficient de clare. Grupai n fiecare modul numai funcii i variabile nrudite. Dac
programai ntr-un limbaj orientat pe obiecte, implementai fiecare clas ntr-un modul
separat, cu toate metodele ei. O s tii apoi unde s le cutai, i apoi o s le putei
reutiliza cu uurin n alte programe.
Cheia economiei de timp este asta: cnd vrei s foloseti o funcie trebuie s tii numai
ceea ce face; nu trebuie s o citeti n ntregime ca s vezi cum face acest lucru. De aceea
funcia este mult mai uor de folosit, i apoi poate fi la nevoie rescris n cu totul alt fel,
fr a influena ctui de puin restul programului. (De exemplu nu conteaz dac sortarea
este bubble sort sau quick sort, totul este s fie tot o sortare. Funciile care folosesc
funcia de sortare nu sunt interesate de modul n care aceasta se face). Un alt uria
beneficiu al acestei relative independene ntre feluritele pri este c n momentul n care
un bug este descoperit i scos dintr-una, celelalte vor rmne neschimbate, pentru c nu se
bazau pe felul n care lucra acea parte, ci doar pe ceea ce ea fcea.

Cte funcii sau linii de program trebuie s fie ntr-un modul? Asta este destul de mult o
chestie de gust, dar editorul de texte pe care l folosii poate influena mult alegerea. Dac
avei 24 de linii de ecran (din care 3 folosite la borduri i meniuri) este greu s scriei
fiiere mari, pentru c v plimbai cu greu prin ele. Alta este situaia dac avei 42 sau 80
de linii.
Un editor inteligent reduce handicapul unor fiiere mari, pentru c v permite s va
mutai cutnd automat apariiile funciilor i variabilelor.
Eu personal m descurc cel mai bine cu fiiere ntre 300 i 700 de linii, coninnd pn n
15 funcii.
Minima vizibilitate
Principiul minimei vizibiliti cere ca un obiect s nu fie vizibil dect prilor din program
care au cu adevrat nevoie de el. S vedem nite aplicri ale lui:
Datele trebuie separate ct se poate de mult de corpul programului.
O variant a acestei reguli este ``n sursa programului nu trebuie s apar nici un numr
cu excepia lui 0, 1, sau -1''. Constantele (n particular cele numerice) se mai numesc i
``magic numbers'' (numere magice). Ele trebuie evitate ntotdeauna.
Metoda comun este de a da nume simbolice constantelor undeva la nceputul fiecrui
modul, sau n fiiere speciale (cu const n Pascal i C, cu #define n C, etc.). Numele
dat unei constante are multe avantaje, care sunt expuse n mai toate cursurile de
programare. Noi vom reaminti dect pe unul: o constant face programul mult mai uor de
citit. Aceasta este o aplicare a principiului minimei vizibiliti: tii numai numele
constantei (care trebuie s arate la ce folosete ea), i nu valoarea ei!
Iat un exemplu:
for i:=1 to 128 do persoana[i].nume := '';

i tradus:
for p:=1 to NumarDePersoane do persoana[p].nume := '';

Nu e mai clar a doua oar?


Cum spuneam prile din program interacioneaz te miri cum. Cel mai adesea
interaciunile se fac prin variabile globale, a cror valori pot fi modificate de o funcie i
citite de alta.
De aceea este bine s transmitem funciilor (procedurilor) toate valorile de care au nevoie
prin argumente i s nu le lsm s modifice variabile globale.
Dac vi se pare greu s programai fr variabile globale, aflai c exist limbaje de
programare n care nu exist variabile de loc, i totui se pot face foarte multe lucruri (un
exemplu este Prolog; despre Prolog se crede ndeobte c are variabile, ns o analiz

atent va arta c ceea ce n Prolog se numete variabil nu folosete de loc la acelai


lucru ca o variabil n sensul uzual al limbajelor imperative ca Pascalul).
n realitate este foarte greu adesea s ne descurcm fr variabile globale, pentru c unele
funcii fac modificri att de masive nct ar trebui s aib zeci de parametri i s
returneze tot attea valori (ceea ce multe limbaje nici nu permit, dealtfel!). Tocmai pentru
a circumveni aceste dezavantaje au fost inventate limbajele orientate pe obiecte (alde C+
+), n care n loc s dai unei funcii argumente, grupezi argumentele la un loc ntr-un
obiect (un fel de record din Pascal); funcia nsi devine un cmp al obiectului (se va
numi ``funcie membru''), i n loc s chemi funcia cu cmpurile obiectului ca argumente,
rogi obiectul s-i aplice singur funcia.
Aceast reducere la maximum a vizibilitii din limbajele orientate pe obiecte (cci
cmpurile unui obiect sunt n mod normal accesibile numai funciilor care-i sunt membre)
se numete ``ncapsulare'' i constituie unul din avantajele majore ale programrii
orientate pe obiecte fa de cea ordinar.
Cu disciplin ns se pot aplica beneficiile ncapsulrii i n programe scrise n limbaje
fr astfel de construcii (C, Pascal), cu foarte mult succes.
Spaiile albe; i(n)dentarea
Unii separ prile din programe prin comentarii baroce ca:
{******************************}
{========intrare/iesire========}
{******************************}

Ei bine, puine spaii albe judicios plasate fac mult mai mult bine lizibilitii programului.
Iat care sunt recomandrile mele n ceea ce le privete:
1. Separai fiecare funcie de vecinele ei printr-o linie alb.
2. ntre titlul funciei i corpul ei eu las mereu un rnd n care pun de obicei
comentarii.
3. ntre declaraiile variabilelor locale i corpul funciei las mereu o linie liber.
4. Punei mai multe linii, i eventual un comentariu de o linie de stelue ntre pri
majore ale programului.
5. Am observat c o expresie este mult mai uor de citit dac separ cu spaii unii
operatori de argumentele lor. Observai:
6. for(i=0;i<ultim;i++) if(!a[i]) total++;

fa de:
for (i = 0; i < ultim; i++) if ( ! a[i] ) total++;

7. Pentru a uura depanarea este bine s punei o singur instruciune pe linie! Pentru
c linia de mai sus la depanare va executa ntreg ciclul for la o singur comand
``step'', iar liniile de mai jos vor permite s vedei ce se ntmpl la fiecare pas:
8. for (i = 0; i < ultim; i++)
9.
if ( ! a[i] ) total++;

10. Dac avei prea multe argumente la o funcie scriei aa:


11.
12.

venit(persoana[pozitie_curenta].nume,
departament[persoana[pozitie_curenta].cod_departament],
indexare_salarii);

13.

14. Dac trebuie s spargei o expresie pe dou linii lsai operatorul la nceputul celei
de-a doua linii.
15.
16.

venit_max = persoana[pozitie_curenta].venit
+ crestere_salariu;

17. Logica expresiei rupte pe mai multe linii trebuie s fie clar:
18.
19.
20.

copii := numar_copii( indice[disc_curent, compartiment,


raft_curent],
eticheta);

21. Nu calculai prea multe lucruri ntr-o singur expresie complicat, pentru c dup
aceea n-o s nelegei nici dumneavoastr ce ai vrut:
22.
23.
24.
25.
26.
27.

i := 1;
c := 'y';
while (i < maxN) and not (a[i] < 0) and not (c = 'n') do begin
i := i + 1;
read(f, c)
end

ci:
i := 1;
gata := false;
c := 'y';
while not gata do begin
if i >= maxN then gata := true;
if a[i] < 0 then gata := true;
if c = 'n'
then gata := true;
if not gata then begin
i := i + 1;
read(f, c)
end
end

28. n englez ``to indent'' nseamn ``a zimui''. n romna s-a format barbarismul ``a
identa'' sau ``a indenta''. care nseamn a aranja frumos n pagin un program, n
aa fel nct structura lui s fie limpede. Mai toate limbajele moderne sunt
indiferente la numrul de spaii, aa c folosii-le ca s indicai structura
programului. Exemple se gsesc i mai sus.
Regula de baz este ca o instruciune care depinde de alt instruciune s fie scris
puin mai la dreapta. De pild if arat aa: if <expresie> then
<instructiune1> else <instructiune2>. <instructiune1,2> depind de if.
Un if se scrie deci aa:
if TrebuieSters then
StergeJucator(BazaDeDate, NumarulDeOrdine)
else
calificari(BazaDeDate, NumarulDeOrdine);

29. Dac instruciunea dependent este o instruciune bloc, avei mai multe
posibiliti de a o aranja n pagin. Totul e s alegei una i s o respectai mereu.
30.
31.
32.
33.

for i := 1 to ultim do begin


CalculeazaNota(i);
AlegePostura(i)
end;

sau
for i := 1 to ultim do
begin
CalculeazaNota(i);
AlegePostura(i)
end;

sau
for i := 1 to ultim do
begin
CalculeazaNota(i);
AlegePostura(i)
end;

sau
for ( i = 1; i <= ultim; i++)
{
calculeaza_nota(i);
alege_postura(i);
}

34. Ct de multe spaii trebuie s se foloseasc pentru a indenta? 1 este prea puin, i
structura programului nu apare suficient de clar, 8 este prea mult, pentru c la o
indentare de 4 nivele nu mai ajunge ecranul. Cel mai bine este ntre 2 i 6.
35. Etichetele de la goto sau case se indenteaz puin mai n stnga:

36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.

repeta:
switch(raspuns) {
case 'd':
stiut = DA;
break;
case 'n':
stiut = NU;
break;
case '?':
interogare();
goto repeta;
default: /* alt raspuns */
return;
}

50. Construciile de genul else if sunt o excepie de la regula de indentare. Ele pot
fi scrise n loc de:
51.
52.
53.
54.
55.
56.

if e1 then cutare1
else
if e2 then cutare2
else
if e3 then cutare3
else cutare3

aa:
if e1 then cutare1
else if e2 then cutare2
else if e3 then cutare3
else cutare3
c sunt echivalente cu un case din

pentru
Pascal (switch din C) -- adic un if cu
mai multe condiii, care au aceeai importan.
Comentariile
Comentariile sunt ntr-adevr extrem de utile pentru a uura nelegerea funcionrii unui
program. Rolurile lor sunt multiple, i modul n care se pot folosi extrem de variat.
n primul rnd exist dou feluri de comentarii:

comentarii de linie

comentarii de sfrit de linie

Primele ocup o linie (sau mai multe) de program n ntregime i explic ceva din
funcionarea lui. Iat unde sunt extrem de utile:
1. Orice fiier (modul) trebuie s conin pe prima sau a doua linie un comentariu
artnd numele fiierului i explicnd pe scurt la ce folosete. Cnd fiierul este
scos la imprimant se acest comentariu indic proveniena sursei.

2. Orice fiier este bine s conin la nceput comentarii descriind autorul, data i
versiunea ultimei modificri ale textului. Exist programe speciale (``Source Code
Control System'' -- control al codului surs) care ajut pstrarea i folosirea
tuturor versiunilor unui program. n cazul n care mai mult lume lucreaz la un
proiect sunt extrem de utile.
3. Fiecare funcie trebuie sa aib o descriere a comportrii ei. Vedei mai jos
seciunea despre funcii. Aceste comentarii se pot ntinde pe mai multe linii.
4. Algoritmii mai compleci trebuie s fie descrii printr-un comentariu care explic
sumar comportarea lor. Acest comentariu n mod normal se plaseaz naintea
zonei de cod care implementeaz algoritmul.
5. Comentariile se indenteaz la fel cu liniile de cod de care in:
6.
7.
8.

for i := 1 to sfirsit do
{ verific toate pozitiile }
verifica( pozitie[i] );

Comentariile de sfrit de linie n general explic operaia fcut pe linia curent. Ele sunt
mult mai greu de ntreinut, pentru c schimbarea uneia din linii stric ntreaga lor
aliniere.
1. Ele trebuie s nsoeasc obligatoriu declaraiile variabilelor globale:
2. var
3.
persoane,
4.
locuri,
5.
LocCurent: integer;

{ numar de participanti
}
{ numarul de scaune la masa
}
{ locul pe care incerc sa pun }

6. Orice linie care face o aciune mai deucheat (al crei sens nu este limpede)
trebuie s fie comentat:
7.
8.
9.
10.
11.

/* elementele nu pot fi 0 */
for ( i = 0; i < nr_elem; i++ ) {
if ( a[i] == 0 ) break;
/* eroare! opresc procesarea */
if ( a[i] < a[i+1] ) swap (&a[i], &a[i+1]);
}

12. Variabilele locale ale unei funcii trebuie s fie cteodat comentate.
13. Mai rar se comenteaz argumentele unei funcii n declaraia ei:
14. function arata(x, y, {coordonatele din stinga-sus}
15.
lungime, latime: integer): boolean;

16. Blocurile la mare distan de testele care le controleaz trebuie s fie comentate
astfel (vedei la else):
17. if ( functionare == INTERACTIV ) {

18.
int alege;
19.
20.
printf("\a\a\a");
21.
alege = meniu_principal();
22.
switch (alege) {
23.
case FISIER:
24.
fisier();
25.
break;
26.
case CALCUL:
27.
calcul();
28.
break;
29.
default:
30.
return 0;
31.
}
32. }
33. else {
/* aici
34.
...
35. }

/* un pic de galagie */
/* selectia
*/
/* toata intrarea/iesirea */

/* nimic valid -> termin */


functionare != INTERACTIV */

Variabilele, constantele, funciile, procedurile


Dac nu poi s-i dai un nume unei variabile nseamn c acea variabil nu este bun.
Reconsider arhitectura ntregului program.
ntr-adevr, nu trebuie s faci nici un efort ca s-i aduci aminte ce reprezint o variabil;
numele ei trebuie s-i spun tot ce te intereseaz.
Nu facei niciodat economie nghesuind n aceeai variabil mai multe valori, depinznd
de context! Fiecare variabil trebuie s reprezinte un singur lucru n fiecare clip.
Compilatoarele moderne sunt suficient de inteligente ca s transforme cele dou variabile
n una singur dac asta se poate! (De fapt le chiar uurai munca nenghesuind mai multe
lucruri laolalt).
Numele unei variabile trebuie s arate exact ce reprezint valoarea ei.
Numele variabilelor, funciilor, procedurilor, argumentelor lor, trebuie s fie de asemenea
uor de reamintit. Dac scrii un program i trebuie mereu s te duci la prima pagin ca s
vezi dac o variabil se cheam max_n. sau max_numere sau maximum_numere treaba
merge destul de greu. n principiu nu se folosesc prescurtri. Un nume de variabil ca x
sau total este absolut inutil, cci orice poate fi x. Scriei x_fereastra sau total_venit
de pild.
Nu trebuie s va fie lene s tastai. De altfel un editor foarte bun (ca de exemplu GNU
Emacs) v ajut foarte mult (Emacs la apsarea tastei M-/ ncearc s completeze
cuvntul curent cu un sufix care se regsete undeva prin text. De pild dac am definit
variabila complexitate_totala. atunci probabil ajunge sa tastez com i apoi M-/ ca
Emacs s completeze ntreg numele).
Nu scriei funcii care n funcie de un argument fac dou lucruri diferite:
int operatie(FILE * f, struct persoana * p, int actiune)
/* daca actiune = 0 -> cauta de cite ori apare persoana p in fisier

10

* daca actiune = 1 -> sterge toate aparitiile p din f


*/

Scriei dou funcii, pe care le chemai dintr-un if sau switch (case).


Este util s folosii notaii diferite pentru constantele numite pentru a le distinge de
variabile. n C, n care conteaz literele mari/mici, aceste constante au de obicei numele
scrise numai cu majuscule.
Un program cu nume de variabile eficient alese are nevoie de mult mai puine comentarii,
i este mult mai uor de citit i ntreinut. Cineva spunea c pot fi citite la gura sobei, ca o
carte bun. Am avut ocazia s citesc astfel de programe i trebuie s recunosc c am
nvat extrem de mult din ele. Pentru curioi recomand doi montri sacrii ai programrii
cristaline, Andrew S. Tanenbaum (autorul sistemelor de operare MINIX i Amoeba) i
Richard Stallman (autorul compilatorului GCC i al lui GNU Emacs).
Dac scriei module care export multe variabile sau funcii putei prefixa toate obiectele
unui modul cu un text care le arat proveniena i semnificaia. De exemplu toate
procedurile care lucreaz cu ferestre se vor numi ceva de genul Win_xxx (win de la
``window''.. Aceasta complic i simplific deopotriv unele lucruri. E mai simplu pentru
c poi avea o procedur make i pentru ferestre, i pentru stive, numai c una se cheam
Win_make iar alta Stk_make.
Dac v decidei pentru prefixe, n general ele sunt preferabil sufixelor. Adic e mai bine
Win_make dect make_win. pentru c prefixele se vd mai uor. n nici un caz nu
amestecai ntr-un program cele dou feluri de numire.
Apropos de amestecare, facei la nceput nite decizii de numire omogen a obiectelor cu
care operai. De exemplu: ``toate comentariile sunt n englez'', sau ``toate numele de
proceduri sunt cuvinte englezeti'', .a.m.d. n program coexist greu o procedur
numar_persoane cu una first_person. (Dac nu tii englez lucrurile sunt ceva mai
simple.)
Fiabilitatea
``Nu avea ncredere n nimeni, nici mcar n tine nsui!'' Respectarea acestei reguli aduce
nite beneficii greu de imaginat. Ce nseamn ea pentru un programator?
1. Funciile nu trebuie niciodat s se bazeze pe cel care le cheam, cum c le
furnizeaz argumente corecte. Verificai toate argumentele nainte de a le prelucra;
dac nu sunt corecte anunai eroarea, fie scriind ceva pe ecran, fie returnnd o
valoare indicnd eroare utilizatorului.
2. Funciile nu trebuie s se bazeze niciodat pe cele pe care le cheam. De fiecare
dat cnd ai chemat o alt funcie verificai dac nu cumva a semnalat o eroare.
Dac da acionai n consecin, i oprii procesarea.
3. Un caz particular al lui 2. este apelul unor funcii din biblioteci. Mai ales cele care
fac intrare/ieire pot eua din zeci de motive. Verificai mereu dac au funcionat
11

nainte de a v arunca cu capul nainte. Dac v e lene, scriei funcii ``nveli''


(``wrappers'') care le cheam pe cele de bibliotec i care verific erorile. Folosii
apoi numai wrapper-ele. De exemplu:
4. void * aloca_memorie(size_t marime)
5.
/* aloca memorie. eroare fatala daca nu mai este memorie
*/
6. {
7.
void * p; /* aici alocam temporar */
8.
9.
p = malloc(marime);
10.
if (p == NULL) {
11.
/* standardul spune ca malloc intoarce NULL
numai
12.
* daca nu a reusit sa aloce cantitatea indicata
*/
13.
fprintf(stderr, " Alocare nereusita!\n");
14.
exit(1);
15.
}
16.
return p;
17. }

sau (n TURBO-Pascal):
procedure deschide_fisier(var f:text; nume:string);
{ deschide un fisier; verifica erorile }
begin
{$i-}
{ inhiba erori de executie pentru intrare-iesire }
assign(f, nume);
reset(fis);
{$i+}
{ inversul lui $i- }
if ioresult <> 0 then begin
writeln('deschide_fisier: Nu pot deschide fisierul ',
nume);
halt
end
end;

18. Din loc n loc introducei verificri asupra datelor pe care le prelucrai, chiar dac
vi se par inutile. n C exist funcia standard assert care este extrem de util, dar
putei scrie i n Pascal una asemntoare. Aceast funcie oprete definitiv
programul dac este chemat cu un argument 0. Frumuseea este c toate
invocrile lui assert sunt scoase definitiv din program doar definind macro-ul
NDEBUG.
Metoda universal este urmtoarea: definii constanta boolean (sau ntreag)
DEPANARE, pe care o facei true. Apoi n program verificai mereu astfel:
if DEPANARE then begin
{ verificam daca locasele sunt bine initializate }
for i := 1 to spatii do
if locas[i].urmator = nil then begin
writeln('Locas cu "urmator" nil la indicele ', i);
halt

12

end

end

Testele pentru locae se vor efectua numai dac DEPANARE este true. Dac facei
DEPANARE false, testele dispar cu desvrire! Putei astfel s v depanai
programul, iar cnd merge s-l scurtai. Un compilator cu optimizri va observa c
liniile de test nu se vor putea efectua niciodat (pentru c DEPANARE este o
constant), i le va scoate cu totul din program. (Pe de alt parte unele teste este
nelept s rmn pentru totdeauna n program; nu se tie niciodat pentru ce date
de intrare programul o ia razna. Este recomandabil s gseti o eroare ct mai
repede i s urlii c ai gsit-o dect s o ignori o vreme i ea s fac prpd n
linite. Muli utilizatori vor aprecia.)
19. Distingei n programele dumneavoastr (cel puin) dou tipuri de erori: fatale i
recuperabile. Scriei dou funcii care trateaz aceste tipuri de erori. Toate erorile
ar trebui s indice ct mai clar locul unde s-au produs: numele programului,
numele modulului, numele funciei, tipul erorii. La depanare nu ajut prea tare
dac vezi ceva de genul: ``eroare de intrare/iesire''. Mai bine e ``statistic: functia
`citeste_data': eroare: nu pot deschide fisierul `info.txt'''.
Tocmai din aceast cauz nu orice eroare trebuie semnalat ca fiind fatal. Nu poi s faci
o mprire la 0? Anun doar pe funcia care te-a chemat cu argumente greite c ceva nu
e n regul. Cum ar fi un program de calculator de buzunar care s-ar opri la o astfel de
eroare? Penibil! Eroarea trebuie doar s fie anunat i procesarea continuat.
Poate vom reveni cu un articol special despre tratamentul erorilor, pentru c este un
subiect extrem de interesant, i foarte prost tratat n literatur. O eroare se poate adesea
propaga n sus pe un lan de funcii care s-au chemat ntre ele (mai ales dac una era
recursiv) la distane extrem de mari de locul producerii. Cum se face asta cu grij, cum
se semnaleaz erorile cel mai simplu, asta merit discutat mai pe-ndelete.
Documentaia
Donald Knuth (unul din cei cu citatele din introducere) este unul dintre adepii a ceea ce
se numete ``litterate programming''. Acesta este un sistem (numit Web) n care
programul i documentaia sa se scriu dintr-un singur foc, apoi cu dou compilatoare
diferite se obin executabilul i cartea de descriere a produsului. Paradoxal, aceast
metod mrete productivitatea i fiabilitatea, pentru c i poi introduce explicaiile
detaliate chiar n corpul programului n timp ce-l scrii, gndind mai uor att asupra
programului ct i documentaiei.
Dac nu spunei cum face ceva un program de-al dumneavoastr, trebuie totui s spunei
ce face. Chiar dac nu scriei programe pentru vnzare este un obicei extrem de util s
scriei scurte documentaii pentru utilizatorii lor sau cei care vor s le modifice. Un model
mpmntenit este pagina de manual interactiv din UNIX (sau comanda ``help'' din DOS,
ntr-un fel). O descriere a programului dumneavoastr ar trebui s conin urmtoarele
informaii:
1. numele programului;
13

2. modul de lansare (argumente, etc);


3. o descriere a comportrii;
4. pentru programe interactive indicaii sumare de folosire;
5. informaii despre fiierele pe care le folosete i structura lor;
6. versiunea curent;
7. autorul programului cu adresa lui (nu se recomand dac programul este un
virus);
8. defeciuni cunoscute;
9. documentaii nrudite.
Portabilitatea
Dac trebuie s folosii trsturi ale limbajului care nu sunt standard (de exemplu n
Turbo C gotoxy(), atunci nu le chemai niciodat direct; scriei pentru ele ``wrappere''.
Astfel de wrappere ar trebui s asigure o comportare uniform a funciilor, chiar dac
bibliotecile de care dispunei se schimb.
De exemplu dac facei un program pe PC care lucreaz cu mai multe culori, dac vrei
s mearg bine i pe plci monocrome i color scriei o funcie culoare_text() cam aa:
void culoare_text(int culoare)
/* alege culoarea textului in functie de numarul
de culori disponibile (2 sau 16) */
{
int cul_reala;
/* MONOCROM este aflat de exemplu intrebind utilizatorul
la inceputul programului */
if (MONOCROM) cul_reala = culoare / 8;
/* daca avem numai 2 culori pastram numai primul bit */
else cul_reala = culoare;
textcolor(cul_reala);
}

Dac v concepei programele ntr-un mod ct mai general vor deveni adesea i ceva mai
uor de scris, i cu siguran foarte uor de ntreinut i ``portat'' (mutat de la un
compilator la altul).
Subiectul este foarte generos, iar tratarea pe care i-am dat-o aici este departe de a fi
exhaustiv. Numai experiena v va convinge de utilitatea unei discipline n actul
programrii. Deci spor la treab!

14

15

S-ar putea să vă placă și