Tutorial Pascal
Tutorial Pascal
Interfata Turbo Pascal 7.0 Mediul integrat (IDE) Turbo Pascal prezinta o bara de meniu orizontala, in partea de sus (meniul principal). Are 10 optiuni:
File Face cam ce face orice meniu File din zilele noastre. Deschide fisier nou, salveaza, etc. Edit Ce credeti ? idem Search Meniul de cautare al aproape oricarui editor de text. Run Ofera modalitati de rulare a programului (normal, pas cu pas), oprirea lui pentru modificari si posibilitatea de a porni programul cu anumiti parametri. Compile Compileaza programul (transforma codul sursa in cod masina, pentru a putea fi inteles si rulat de procesor) si faciliteaza crearea unui proiect, prin setarea unui "primary file" (programul care e rulat cand utilizatorul da aceasta comanda este cel setat prin primary file, asa ca nu mai aveti probleme cand dati Ctrl+F9, daca sunteti intr-un unit. Debug Vizualizarea valorilor variabilelor unui program, in timpul rularii acestuia, afisarea ultimului ecran al programului, puncte de intrerupere (breakpoint-uri), etc Tools Cateva programe care pot ajusta crearea programului, prin modificarea parametrilor pentru crearea lui, verificarea programului pentru a vedea unde "papa" mult timp, etc. Options Puteti seta directoarele implicite pentru unituri, culorile folosite de editor, salvarea preferintelor si (foarte) multe altele. Window Posibilitati de aranjare a ferestrelor Help Destul de usor de inteles ce face asta... doar ca Helpul e in engleza (sper ca nu se astepta cineva sa fie in romana )
E clar ca, in majoritatea timpului, nu veti avea nevoie de cele mai multe din optiunile oferite de meniu, asa ca am sa dau mai jos o lista de scurtaturi de la tastatura pentru cele mai uzuale functii:
ALT + X F2 F3
inchide Borland Pascalul (l-am pus primul pentru a putea iesi repede din el, daca aveti probleme salveaza fisierul curent deschide un fisier (unul nou sau unul salvat) )
comanda CUT comanda COPY comanda PASTE arata ultimul ecran al programului (ultimele afisari) verifica erorile de sintaxa si compileaza programul reseteaza programul (in caz ca il aveti pornit in modul pas cu pas) pentru alte modificari adauga o variabila a carei valoare va fi afisata pe parcursul executarii programului cu cursorul pus pe un anumit cuvant, va deschide Help-ul la sectiunea respectiva cu cursorul pus pe un cuvant care e numele unui fisier cu extensia .PAS din directorul curent, deschide si acel fisier (de ex. un unit)
CTRL + F9 verifica erorile de sintaxa, compileaza si ruleaza programul CTRL + F2 CTRL + F7 CTRL + F1 CTRL + ENTER
La lucru in Turbo Pascal 7.0 Pentru a incepe un program nou, in primul rand trebuie sa aveti Pascalul pornit. Selectati File / New si veti avea un nou fisier, denumit NONAMEXX.PAS (XX simbolizand al catelea fisier nou deschis este acesta). Alta solutie este sa alegeti File / Open, introduceti un nume, iar fisierul va fi automat salvat (bineinteles, gol).
Acum ca aveti in fata tot ce va trebuie, nu aveti DECAT sa scrieti programul, sa-l verificati de erori (poate fi verificat de erori compilandu-l, dar si rulandu-l direct, fiindca inainte de a-l rula il si verifica... ce descoperire )
Foarte "originalul" program care afiseaza Hello World este mai jos. Spor la joaca
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
4. End.
Veti invata in urmatoarele tutoriale ce inseamna si ce fac aceste 4 linii de mai sus, momentan e de ajuns sa stiti ca puteti modifica Hello World de mai sus cu orice text vreti voi si ca va trebui sa apasati tasta Enter pentru a iesi din program.
Cele trei parti principale ale unui program Pascal sunt: antetul (header-ul) programului, declaratiile in care se descriu obiectele asupra carora vor avea loc prelucrarile (in cadrul programului pe care il analizam, vom considera numai declararea variabilelor) si partea principala a programului in care se scriu instructiunile (ansamblul operatiilor ce urmeaza a fi executate). O structura vizuala a unui program Pascal ar arata in felul urmator:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. PROGRAM Nume_Program;
2. 3. CONST 4. (* declaratii de constante *) 5. 6. TYPE 7. (* declaratii de tipuri *) 8. 9. VAR 10. (* declaratii de variabile *) 11. 12. (* declaratii de subprograme (functii si proceduri) *) 13. 14.BEGIN 15. (* instructiuni *) 16. END. De remarcat ca un program functional necesita doar ultimele 3 linii din schita de mai sus, iar compilatorul nu va avea nimic impotriva. Bineinteles, nu veti putea folosi variabile, constante, tipuri sau subprograme. Pentru a intelege aceasta structura, deloc complicata, sa analizam fara graba un program Pascal care calculeaza si afiseaza volumul unui paralelipiped. Este vorba de un program care declara variabile, citeste valorile acestora, efectueaza un calcul simplu si afiseaza rezultatul.
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. PROGRAM volumul;
2. {calculeaza si afiseaza volumul unui paralelipiped} 3. VAR lungime, latime, inaltime, volum: real; 4. BEGIN 5. writeln('Introduceti valori: lungime, latime, inaltime'); 6. readln(lungime, latime, inaltime); 7. volum := lungime * latime * inaltime; 8. writeln('Volumul paralelipipedului= ', volum:9:5,' metri cubi')
9. END. {volumul}
Cand programul este rulat, pe ecran se va afisa:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. Introduceti valori: lungime, latime inaltime 2. 8.3 7.25 6.29 3. Volumul paralelipipedului= 378.50075 metri cubi De remarcat ca sirul "8.3 7.25 6.29" a fost introdus de la tastatura, fiind valorile pentru lungime, latime, respectiv inaltime. Examinand acest program, vom putea vedea urmatoarele elemente: Antetul (header-ul) programului Prima linie contine antetul programului. Program este un cuvant rezervat cu care incepe (optional) un program (Borland) Pascal. El este urmat de numele programului: volumul si de simbolul punct si virgula. Simbolul punct si virgula (" ; ") este un separator de instructiuni. Este strict necesar, bineinteles, cu unele mici exceptii, care le voi semnala la momentul potrivit. O restrictie importanta a identificatorilor este aceea ca nu pot contine in interiorul lor blancuri (spatii). Comentariile A doua linie a programului "volumul" este numita un comentariu. Orice sir de caractere delimitat de seturile { si } sau (* si *) reprezinta un comentariu. Comentariile sunt folosite pentru a mari inteligibilitatea programelor (ele fiind ignorate de compilatorul Pascal), usurand astfel munca de intelegere si corectare a acestora. Daca se doreste imbricarea comentariilor, acest lucru se face alternand acoladele cu (*. Ex. modul corect:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. (* comentariu1 {comentariu imbricat} comentariu2 *) 2. { comentariu1 (*comentariu imbricat*) comentariu2 } modul incorect
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
Remarcati in ultima linie a programului prezenta comentariului {volumul}, tocmai pentru a reaminti cititorului numele programului care s-a incheiat. Comentariile pot fi plasate oriunde in cadrul programului, dar nu in interiorul identificatorilor. Declararea variabilelor Cuvantul cheie var (prescurtare de la variables) incepe actiunea de declarare a variabilelor. Fiecare variabila trebuie sa poarte un nume. Programul volumul foloseste patru variabile (lungime, latime, inaltime, volum) identificate in faza de analiza structurata a programului. Acestea sunt grupate astfel: Variabile de intrare: lungime: lungimea paralelipipedului latime: latimea paralelipipedului inaltime: inaltimea paralelipipedului Variabile de iesire: volum: volumul paralelipipedului Nu trebuie sa va speriati de denumirea pompoasa (de intrare, de iesire). Variabilele se declara la fel, indiferent de rolul lor. Denumirile sunt folosite doar pentru a usura intelegerea rolului acestor variabile in program. Tipuri de date In Pascal exista doua tipuri (simple) de date pentru variabile numerice: intregi si reale. Variabilele intregi pot lua valori numai numere intregi, cum ar fi: 6, -19, 0 sau 73700. Un calculator poate reprezenta numai o submultime finita de intregi. O valoare intreaga poate fi salvata in memoria unui calculator doar daca respectiva valoare apartine intervalului (-maxint1, maxint), undemaxint este o constanta predefinita. In majoritatea implementarilor limbajului Pascal constanta maxinteste egala cu 32767. Daca o variabila Pascal va avea o valoare fractionara, variabila trebuie sa fie de tip real. Intr-un program (Borland) Pascal tipul fiecarei variabile utilizate trebuie sa fie declarat. Omiterea declararii unei variabile folosite in partea principala a programului va genera o eroare. In programul pe care-l analizam (volumul), declararea variabilelor s-a facut utilizand enuntul:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
Enunturile (instructiunile) write / writeln Enunturile write / writeln sunt utilizate pentru afisarea pe un suport de informatie (de regula monitorul) a rezultatelor obtinute in urma prelucrarilor, a mesajelor catre utilizator etc. Write si WriteLn reprezinta in Pascal doi identificatori (de procedura) predefiniti (standard). De remarcat ca daca dati unui program numele "write" (sau "writeln"), nu mai aveti voie sa folositi in program nici un enunt write (sau writeln) cu sensul predefinit (adica sa afiseze un text, variabila, etc). Ori de cate ori se va executa un enunt write / writeln, calculatorul va afisa ad-litteram tot ceea ce este inclus intre apostrofuri, precum si valorile oricarei variabile sau expresii aflate intre paranteze. In plus fata de write, Writeln va trece la linie noua dupa afisarea propriu-zisa. Pentru separarea elementelor din corpul instructiunii se folosesc virgulele. Primul enunt din programul analizat (volumul) determina calculatorul sa scrie pe ecran mesajul:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. Introduceti valori: lungime, latime inaltime dupa care va muta cursorul la inceputul liniei urmatoare. Fara un astfel de mesaj este greu sa ne dam seama ca este timpul sa se tasteze ceva. Writeln forteaza calculatorul sa afiseze tot ceea ce s-a specificat in interiorul parantezelor si sa treaca la linie noua. La un enunt write, cursorul va ramane pe pozitia imediat urmatoare ultimul caracter scris cu acest enunt. Writeln poate fi folosit si fara parametri pentru a muta cursorul cu o linie mai jos. Enuntul readln In momentul in care s-a executat instructiunea
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. volum :=lungime*latime*inaltime; se numeste enunt de atribuire intrucat el calculeaza ceva (produsul dintre lungime, latime si inaltime) si atribuie (asigneaza, afecteaza) rezultatul variabilei din stanga simbolului ":=". Partea dreapta a acestui enunt este un exemplu de expresie aritmetica. Formatarea afisarii Ultimul enunt din programul volumul:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
Nu poate contine anumite caractere speciale (~ ! @ # $ % ^ & * ( ) + ` - = { } [ ] : " ; ' < > ? , . / |), multe avand intelesul lor in Pascal. Unele compilatoare de Pascal nu pun nicio restrictie asupra lungimii identificatorilor, dar altele folosesc doar primele cateva caractere (Borland Pascal foloseste primele 63 de caractere - n-am testat niciodata ). Problema des intalnita la identificatorii lungi este faptul ca incetinesc scrierea programului de catre programator. Lungimea identificatorilor este bine sa fie limitata la 10, maxim 15, caractere, in majoritatea cazurilor. Constante Constantele sunt identificatori ai unor valori ce sunt definite la inceputul programului. Valorile asignate constantelor nu pot fi schimbate in decursul rularii programului. Mai jos aveti o lista de constante declarate ca: Integer, Real, Caracter, String, Boolean (in aceasta ordine). Aceste tipuri de date vor fi explicate in tutorialul urmator.
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. const numar = 2; 2. alt_numar = 3.14; 3. litera = 'G'; 4. nume = 'Ionut'; 5. DoWait = False;
{numar intreg} {numar real} {caracter} {sir de caractere (string)} {valoare logica (boolean)}
Constantele sunt, de obicei, folosite pentru a tine o valoare des utilizata in program, care s-ar putea sa fie nevoie sa fie schimbata. Ganditi-va ca vreti sa faceti un program care deseneaza un dreptunghi in relief (3D). Pentru aceasta va trebui sa desenati dreptunghiul de mai multe ori, cu diferite culori, pentru a realiza efectul 3D. Daca la primul dreptunghi veti pune marimea laturii 50, la al doilea 49 si tot asa, veti vedea ca atunci cand vreti sa modificati programul, va trebui sa modificati fiecare numar. Alternativ, cu constante, in secventa de cod de mai jos:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. { ... } 2. const latura_mare = 50; 3. latura_mica = 20; 4. { ... } 5. begin 6. { ... } 7. Deseneaza Dreptunghi (latura_mare, 8. Deseneaza Dreptunghi (latura_mare 9. Deseneaza Dreptunghi (latura_mare 10. Deseneaza Dreptunghi (latura_mare 11. Deseneaza Dreptunghi (latura_mare 12.{ ... } 13. end.
2, 4, 6, 8,
2) 4) 6) 8)
Atentie: acest program nu va rula in Pascal. Este dat doar pentru exemplificare. Secventele { ... } desemneaza cod care e posibil sa existe.
Variabile Variabilele sunt identificatori ai unor valori de anumite tipuri, specificate in declaratie. Spre deosebire de constante, pot fi declarate mai multe variabile de acelasi tip pe o singura linie. Mai jos aveti o lista de declarare de variabile. Aceste tipuri de date vor fi explicate in tutorialul urmator.
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. var index, numar, suma, produs: Integer; 2. valoare_pi, valoare_e: Real; 3. litera: Char; 4. nume, prenume: String; 5. AmTerminat: Boolean;
Variabilele sunt folosite pentru valori care se pot schimba in decursul programului. Variabilele pot fi folosite si in loc de constante, declarandu-le valoarea in program. Asignare Operatia de asignare este operatia prin care variabilele primesc valori. Pentru a asigna o variabila se foloseste urmatoarea sintaxa:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. variabila := valoare; Secventa := se numeste operatorul de asignare. valoare trebuie sa fie de acelasi tip cu tipul declarat al variabilei. Exemple (folosind declararea de variabile de mai sus):
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
4. 5. 6. 7. 8.
litera := 'k'; litera := #65; {caracterul A majuscula} nume := 'Bitcell'; prenume := '.info'; nume := nume + prenume; {da, sirurilor de caractere li se poate aplica operatorul de adunare, caz in care sirurile vor fi concatenate} 9. {in acest exemplu, variabila "nume" va primi valoarea "Bitcell.info"} 10. 11. AmTerminat := (litera = 'X') and (litera = 'x'); {comparatii care returneaza un rezultat boolean, True sau False} 12. 13. valoare_pi := 22 / 7; {nu este chiar valoarea lui pi, dar pentru un exemplu merge} 14. 15. valoare_e := (numar / index) + suma; 16.
Operatie adunare sau semn pozitiv scadere sau semn negativ inmultire impartire catul impartirii intregi restul impartirii intregi
Operanzi reali sau intregi reali sau intregi reali sau intregi reali sau intregi intregi intregi
Rezultat real sau intreg real sau intreg real sau intreg real intreg intreg
Dupa cum se vede in tabelul de mai sus, div si mod pot fi folosite doar cu numere intregi. Operatorul de impartire / poate fi folosit si cu numere intregi, si cu numere reale, dar va returna intotdeauna un numar real. Celelalte operatii functioneaza pe si numere intregi, si pe numere reale. De tinut minte e faptul ca atunci cand intr-o operatie folosim si numere intregi, si numere reale, rezultatul va fi intotdeauna un numar real. Fiecarei variabile ii poate fi asignat o valoare de acelasi tip cu ea. Acesta este motivul pentru care nu se poate asigna o valoare reala unui intreg. Totusi, anumite tipuri de date vor converti valoarea catre un tip mai mare de date. Asta se intampla, de exemplu, cand se asigneaza o valoare intreaga unei variabile reale.
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. numar_intreg := 170;
2. numar_real := numar_intreg; In acest caz numarul intreg 170 va fi convertit in numar intreg, iar variabila numar_real va avea valoarea170.0. Schimbarea unui tip de data in alt tip se numeste typecasting (selectare de tip). Typecasting-ul din exemplul de mai sus este un exemplu de typecasting implicit (se intampla fara a se specifica aceasta intentie de catre programator). Majoritatea compilatoarelor moderne de Pascal suporta si typecasting-ul explicit, cu o sintaxa doar putin diferita de cea a limbajului C. Codul de mai jos da un exemplu al typecasting-ului explicit.
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
6. write(numar8biti); 7. end.
In acest exemplu asignam o valoare intreaga, reprezentata intern pe 16 biti, unei variabile intregi reprezentata pe doar 8 biti, specificand ca vrem sa schimbam tipul variabilei in byte (reprezentare interna de 8 biti). Acesta este typecasting-ul explicit si, in Pascal, se face prin a pune paranteze in jurul valorii careia ii vrem schimbate reprezentarea si punand tipul dorit in fata parantezei. In exemplul de mai sus, variabila pe 8 biti va primi valoarea de 232 (byte-ul cel mai putin semnificativ din valoarea pe 2 bytes initiala). Punctuatie si indentare Avand in vedere ca Pascal ignora spatiile albe (vezi mai sus), punctuatia este necesara pentru a spune compilatorului unde se termina o instructiune. Caracterul care indica terminarea unei instructiuni este, in general, punct si virgula ;. Caracterul care indica terminarea programului este punctul .. Trebuie sa aveti un caracter ; dupa: numele programului fiecare definitie de constanta fiecare definitie de variabila fiecare definitie de tip aproape toate instructiunile Ultima instructiune inainte de o instructiune end nu necesita sa aiba ; la sfarsit. Totusi, nu se intampla nimic sa puneti si in acest caz. De fapt, e chiar recomandat sa puneti in acest caz, datorita faptului ca e posibil sa adaugati o alta instructiune dupa instructiunea care nu are terminator ( ; ) si va veti trezi cu erori de compilare. Indentarea NU este necesara, din punctul de vedere al compilatorului. Cu toate acestea, indentarea este una dintre cele mai importante lucruri in programare, oferind claritate codului. Alta problema cu indentarea este indentarea facuta folosind tasta TAB. Caracterul TAB, fiind afisat in diferite feluri in diferite editoare, duce la neclaritati si/sau imprastierea codului pe latime. (Eu, ca programator, refuz sa lucrez pe o bucata de cod care nu e scrisa cat mai clar posibil. Daca e strict necesar sa lucrez pe ea, primul lucru de care ma ocup este indentarea codului.) Alt lucru important (si care ajuta la claritate) e punerea fiecarei instructiuni pe linie separata. Sa luam o bucata de program si sa vedem ce si cum. Aceasta este bucata de cod, indentata si clara.
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. uses crt; 2. const spaced = ' '; 3. select = ' * '; 4. var c: char; 5. sel: integer;
6. begin 7. clrscr; 8. c := #0; 9. sel := 1; 10. {afisam meniul initial} 11. TextColor(Yellow); 12. TextBackground(Blue); {galben pe albastru pentru optiunea selectata} 13. WriteLn(select, '1. BitCell.info'); (luat dintr-un exemplu)
1. 2. 3. 4.
uses crt; const spaced = ' '; select = ' * '; var c: char; sel: integer; begin clrscr; c := #0; sel := 1; {afisam meniul initial} TextColor(Yellow); TextBackground(Blue); {galben pe albastru pentru optiunea selectata} WriteLn(select, '1. BitCell.info');
1. var a, b: integer;
2. begin 3. write('Dati a: '); readln(a); 4. write('Dati b: '); readln(b); 5. write('Suma celor doua numere este ', a + b); 6. readln; 7. end. , nu intalnim alta structura de calcul decat cea secventiala. Cu asta vreau sa spun ca toate instructiunile vor fi folosite, pe rand, in ordine, fara ca vreuna din instructiuni sa se repete. Suna ca o reteta usoara de mancare, nu ? Exact asa si este. Structura alternativa Este o structura de calcul care permite, in functie de anumite conditii, executarea (sau neexecutarea) unei anumite instructiuni sau secventa de instructiuni. Programul de mai jos vrea sa faca suma a doua numere, in afara de cazul in care primul numar este 0 (zero), caz in care aduna al doilea numar cu el insusi :
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. var a, b: integer;
2. begin 3. write('Dati a: 4. write('Dati b: 5. If a <> 0 Then 6. Else 7. readln; 8. end. '); readln(a); '); readln(b); write('Suma celor doua numere este ', a+b) write('Suma celor doua numere este ', b+b);
Deci, am pus o conditie si in functie de ea executam o parte sau alta a codului. Parca suntem in viata. Ne ofera cineva ceva, dar noi nu acceptam decat daca pretul e mai mic sau egal cu o anumita suma, pentru ca altfel ni se pare prea mare pretul. Structura repetitiva Este o structura de calcul care permite, in functie de anumite conditii, executarea unei anumite instructiuni sau secventa de instructiuni de N ori (unde N poate fi 0, 1 sau mai mult). Programul de mai jos citeste doua numere de la tastatura si va aduna primul numar cu el insusi de atatea ori cat e valoarea celui de-al doilea :
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
9.
10. end.
readln;
Este simplu, doar adunam valoarea din A de B ori, altfel spus inmultim A cu B. Totusi, scopul acestui program nu e de a face inmultirea, ci de a explica structura repetitiva Aceste trei structuri de calcul s-au dovedit de-a lungul timpul a fi, intr-adevar, singurele strict necesare crearii unui program, motiv pentru care nici nu o sa explic in aceste tutoriale cum se folosesc, in Pascal, salturile neconditionate (desi exista aceasta posibilitate). Instructiuni conditionale Avem la dispozitie doua tipuri de instructiuni conditionale in Pascal: If ... Then ... [Else ... ] si Case. Sa incepem cu inceputul. Structura If ... Then ... [Else ... ] Are doua sintaxe, putin diferite. Cea mai obisnuita este mai jos:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
Atentie:In cazul folosirii acestei sintaxe, semnul " ; " (punct si virgula) este strict necesar dupainstructiune1. Sa exemplificam aceste structuri prin programul de mai jos:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
4. 5.
6.
write('Ce varsta aveti ? '); ReadLn(varsta); write('Sunteti baiat sau fata ? (M / F) : '); ReadLn(sex); If (sex = 'f') or (sex = 'F') Then WriteLn('Sex feminin') Else WriteLn('Sex masculin'); If varsta > 17 Then WriteLn('Persoana majora'); ReadLn;
7. 8. 9.
10.
11. 12.
13. 14.
15. End.
Pentru a intelege programul, sa-l luam pas cu pas. Am definit doua variabile, una de tip integer (valoare numerica) denumita varsta si una de tip Char (un singur caracter) denumita sex. Dupa citirea de la tastatura a valorilor pentru aceste doua variabile, verificam daca variabila sex e cumva litera F. De remarcat ca am folosit operatorul logic OR pentru a verifica daca variabila are valoarea f sauF, ambele insemnand faptul ca sexul este feminin, caz in care afisam acest lucru. Pe ramura de Else, stiind ca sexul nu este feminin, am pus direct afisare ca sexul este masculin, ceea ce nu este tocmai corect. Trebuia facuta o verificare si daca valoarea variabilei sex este m sau M, pentru a asigura acuratetea programului. In programul de mai sus, daca pentru variabila sex introducem litera c, progamul va afisa tot "Sex masculin", dar momentan scopul meu nu e de a crea programe fara bug-uri, ci doar de a va explica modul de functionare a structurilor conditionale. Pentru varsta facem o singura verificare, anume daca varsta este strict mai mare decat 17 (adica minim 18), caz in care afisam "Persoana majora". Sa refacem programul pentru a lua in calcul si alte cazuri (cum ar fi verificarea pentru sexul masculin, varste negative sau peste 150 de ani )
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
Then WriteLn ('Persoana minora') Else If (varsta < 60) Then WriteLn ('Persoana majora') Else If (varsta < 150) Then WriteLn ('Persoana in varsta') Else WriteLn ('Personaj biblic'); ReadLn;
28. End.
Dupa cum vedeti, in noul program am luat in calcul ca variabila sex poate reprezenta sex feminin, sex masculin sau un caracter care nu reprezinta nici unul din acestea doua cazuri. Pentru aceasta, am folosit instructiuni conditionale imbricate (adica una in interiorul alteia). Arata urat, nu ? In plus, e si destul de greu de inteles. Cu toate acestea, pentru variabila varsta am facut chiar si mai multe verificari, folosind multiple instructiuni conditionale imbricate. E deja mai urat si chiar si mai greu de inteles. Nu am de facut decat sa va recomand multa atentie cand creati sau recititi programul ... si sa va prezint instructiunea conditionala multipla, ca o alternativa la instructiunea If ... Then ... [Else ... ] Structura Case Instructiunea Case, denumita si instructiune alternativa multipla, ofera posibilitatea verificarii unei variabile pe un set mai intins de valori, fara a da dureri de cap la crearea sau recitirea programului. Sintaxa generala:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. Case variabila_simpla Of
2. 3. 4. 5. val_posibile1 : instructiune1; val_posibile2 : instructiune2; ... val_posibileX : instructiuneX; 6. Else instructiuneY; 7. End; Sa incercam sa intelegem aceasta sintaxa, care pare a fi ceva mai complicata decat sintaxa instructiunii dinainte. Variabila_simpla desemneaza orice variabila de un tip simplu de date (vedeti tutorialul anterior). Nu poate fi folosit aici, de exemplu, tipul String, el nefiind un tip simplu de date. Val_posibile1, Val_posibile2 si Val_posibileX tin locul unei (sau a mai multor) valori posibile pentruvariabila_simpla. Daca valoarea variabilei variabila_simpla e egala (sau inclusa) cu vreuna din valorile posibile, atunci va fi executat setul corespunzator de instructiuni. Instructiune1, instructiune2 si instructiuneX pot fi orice fel de instructiuni, inclusiv cele compuse. Pe ramura de Else avem acelasi lucru ca si la instructiunea If ... Then ... [Else ... ], adica o anumita instructiune care se executa in cazul in care valoarea variabilei variabila_simpla nu se regaseste in optiunile de mai sus (Val_posibile1, Val_posibile2, Val_posibileX). Aceasta ramura, ca si la If ... Then ... [Else ... ], poate ramane nefolosita.
In Pascal, sunt trei tipuri de instructiuni de ciclare: While Repeat For Bucla While Sintaxa generala a acestei instructiuni este:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. While True Do instructiune; , caz in care nu se poate iesi din bucla din cauza conditiei. Daca nu exista in interiorul acestei bucle o instructiune de iesire (Break), atunci bucla va fi infinita (nu va mai iesi din ea, programul se va bloca si va trebui terminat cu Ctrl + Break, sau si mai rau, din Task Manager sau reboot). In general, conditia va face referire la o conditie care va deveni adevarata, pe masura bucla lucreaza sau este o simpla verificare a unui contor, caz in care putem calcula dinainte exact cate ciclari va face bucla (de cate ori va repeta instructiunile aflate in interior).
De remarcat ca iesirea din bucla se va face doar cand conditia va returna valoarea False. E si destul de usor de inteles asta : While conditie Do instructiune care in limbaj natural ar suna Cat timp conditia_mea_e_adevarata Fa ceva. Aceasta instructiune mai este denumita si "instructiune repetitiva cu test initial" (testul de iesire din bucla se face la inceput). Instructiunile din aceasta bucla nu se vor executa nici macar o data, daca testul de iesire returneaza valoarea False de la inceput. Instructiune poate fi orice fel de instructiune, inclusiv instructiunea compusa. Sa vedem cum am putea face in Pascal, folosind bucla While, programul de mai sus care afiseaza pana la 20.
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
-32768 si 32767), care va fi ceea ce denumim contorul buclei. Initializam contorul cu 1, aceasta fiind prima valoare ce va trebui afisata, apoi afisam contorul, care apoi va fi marit cu o unitate. Pe masura ce avansam, ajungem cu Cnt = 20. Ce se va intampla atunci : va fi afisat (20), contorul va fi crescut la 21 si programul va reveni la conditia de la inceputul buclei : este Cnt mai mic sau egal cu 20 ? Evident, nu mai este cazul, motiv pentru care se trece la executarea urmatoarei instructiuni din program, anume ReadLn. Sa vedem cum am putea face programul daca am initializa Cnt cu valoarea 0.
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
Cred ca acum ati inteles cum sta treaba cu While, so ... next one Bucla Repeat Sintaxa general a instructiunii Repeat este:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. Repeat 2. instructiune; 3. until conditie; Spre deosebire de While, iesirea din aceasta bucla se face atunci cand valoarea conditiei este True. Pentru o bucla infinita se va folosi:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. Repeat 2. instructiune; 3. until False; De remarcat ca instructiune nu e necesar sa fie instructiune compusa. Cu alte cuvinte, nu este necesar
sa puneti Begin ... End pentru a delimita bucla, acest lucru fiind facut de Repeat ... Until. Aceasta instructiune mai este denumita si "instructiune repetitiva cu test final" (testul de iesire din bucla se face la sfarsit). Instructiunile din aceasta bucla se vor executa macar o data, daca testul de iesire returneaza valoarea True la prima testare. Sa rescriem programul initial folosind aceasta instructiune.
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
Instructiuni de control pentru bucle Instructiunea Continue Sa presupunem ca avem o bucla care afiseaza numerele de la 1 la 20, ca si in exemplele de mai sus, dar nu vrem sa afisam numarul 9. Cum putem face acest lucru ? Foarte simplu. Trebuie sa "sarim" peste o executie a buclei, cea in care indexul are valoarea 9, inainte de a afisa numarul. Pentru acest lucru folosim instructiunea Continue. Sa vedem un exemplu, folosind bucla FOR:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
indexul
este
egal
cu
9,
nu
executam
Instructiunea Continue sare la urmatorul ciclu al buclei, indiferent ca e vorba de bucla While, Repeat sau For. Instructiunea Break Ce facem daca avem nevoie sa scriem un program care afiseaza numerele de la 1 la 20, dar numai atat timp cat suma numerelor afisate este mai mica decat 35 ? Pentru acest lucru trebuie sa facem o suma, pe care o crestem cu valoarea indexului la fiecare afisare. Aceasta suma o vom verifica inainte de urmatoarea afisare si, daca este mai mai mare sau egala cu 35, folosim instructiunea Break. Sa modificam programul de mai sus pentru un mic exemplu:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
11.
12. End.
ReadLn;
Acest program va afisa numerele de la 1 la 7, inclusiv, fiindca 1 + 2 + ... + 8 = 36. Instructiunea Break opreste bucla din care este apelata. dar nu poate opri decat o singura bucla. Daca aceasta instructiune este folosita in interiorul unei bucle care este imbricata in alta bucla, doar bucla interioara va fi oprita.
So, punem cuvantul cheie "array", punem indexul de inceput al sirului (poate fi si -17 ), punem ".." pentru a specifica faptul ca e vorba de un interval, punem indexul de sfarsit (1000 in exemplul de mai sus), adaugam cuvantul cheie "of" (asa-i romanul, plin de ofuri face parte din vector ... si nimic mai mai mult. Exemple
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. var test: array [65 .. 122] of char; 2. test2: array ['H' .. 'a'] of boolean;
Nu va speriati, si varianta in care am pus litere pe post de indecsi e functionala. Rar folosita, dar posibila. Deci ... un vector de caractere (cu indecsi intre 65 si 122 ... si un vector de valori logice, intre 'H' si 'a' ... adica un vector cu 25 de pozitii). Cum se spune, nimic mai simplu. Avem variabila sir care are 1000 de pozitii si imi permite sa salvez cate un numar pe fiecare dintre aceste pozitii. Singura (posibila) problema este faptul ca nu putem salva decatnumere intregi in acest sir. Elementele unui vector trebuie sa aiba acelasi tip. Nu se pot defini vectori ale caror elemente sa fie fisiere. Modalitatea de accesare a unei anumite pozitii din sir se face scriind numele sirului (sir in cazul acesta) si, intre paranteze drepte, [ si ], indicele (pozitia) din sir care o dorim.
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. sir[17] := 2342;
In acest fel, fiecare pozitie a vectorului poate fi privita ca o variabila de sine statatoare, scrisa putin mai ciudat. Atentie: Nu confundati pozitia unui element din vector cu valoarea lui. Daca am valorile urmatoare in vector (in ordine): 159, 65, 78, 23, 72, 34 ... atunci elementul de pe pozitia3 are valoarea 78. Atentie maxima, am vazut multe aiureli cauzate de confuzii de acest gen. Alta problema destul de comuna este incercarea de a folosi vectori care ocupa mai mult decat capacitatea de memorie alocata de catre Pascal. Aceste incercari vor determina compilatorul de Borland Pascal 7.0 sa genereze o eroare de compilare cu mesajul "Structure too large". Compilatorul de Borland Pascal 7.0 ofera un spatiu de memorie de maxim 64KB per fisier al programului (momentan, un singur fisier). Exista alte compilatoare care ofera mult mai mult si care, evident, nu dau asemenea erori. Stiind ca un element al unui vector este apelat folosind indexul lui, devine deja clar ca putem folosi bucle pentru lucrul cu vectori. Hai sa vedem cum citim 1000 de numere, folosind vectori si o bucla For (daca nu mai stiti ce e o bucla For, Instructiuni repetitive va mananca ).
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
Write('Dati elementul ', i : 4, ' : '); ReadLn(sir[i]); {... si le salvam in vector} end; ClrScr; {afisam elementele citite de la tastatura}
15. 16.
Pentru amuzament, hai sa dam valorile elementelor din vector in felul urmator: pe o pozitie sa fie valoarea 1, pe urmatoarea sa fie valoarea 0, pe urmatoarea pozitie iar 1, and so on.
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
Raspunsul e simplu: nu stiu Vectorii unidimensionali, intr-un mod cat se poate de evident, aveau o singura dimensiune, lungime, care era definita de numarul de elemente din ei. Hai sa ne imaginam vectorul unidimensional ca pe o linie din foaia de matematica: un sir de casute, nu ? Fiecare casuta accepta o cifra (sau, daca scrii ca mine, trei ), deci fiecare element e de acelasi tip.
Totusi, foaia de matematica are doua dimensiuni, lungime si latime. E un tabel. Cum definesti un vector unidimensional in asa fel incat sa-l poti folosi ca pe un tabel ? Nu o faci. Folosesti un vector bidimensional, care este chiar un tabel, nu ?
P.S. ca informatie relativ utila, vectorii unidimensionali pot fi folositi ca vectori multidimensionali. Memoria video este vazuta, practic, ca un vector unidimensional, dar noi o vedem bidimensional. Asta va spune ceva ?
Hai sa vedem cum definim un vector bidimensional, fiindca sunt sigur ca nici nu va trece prin cap
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. tabel = array [1.. 10, 1 .. 20] of integer; Asemanator cu vectorii unidimensionali, nu ? Primul set (1 .. 10) determina numarul de linii, iar al doilea numarul de (20 de) coloane. Accesul la elementul de pe o anumita pozitie necesita acum doi indecsi, ceea ce e normal:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. tabel[2, 5] := 124; Codul acesta poate fi citit in felul urmator: elementul de pe linia 2, coloana 5 din matricea denumita "tabel" va lua valoarea 124. Hai sa facem o tabla de sah (de 8x8) si sa o umplem, ca si in exemplul de mai sus, cu 1 si 0.
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. uses crt; 2. var sir : array [1 .. 1000] of Integer; 3. tabla: array [1 .. 8, 1 .. 8] of byte; 4. i,j, n: Integer;
5. begin 6. for i := 1 to 8 do 7. for j := 1 to 8 do 8. tabla[i, j] := (i + j) mod 2; 9. 10. for i := 1 to 8 do 11. begin 12. for j := 1 to 8 do 13. write(tabla[i,j]:2); 14. writeln; 15. end; 16. end. Rezultatul acestei bucati de cod poate fi admirat in imaginea urmatoare:
So, cam asta ar fi si cu vectorii bidimensionali (matrice) ... cred ca v-am speriat degeaba sa va dati jos centura de siguranta.
... puteti
Vectorii cu mai mult de 2 dimensiuni se declara la fel (cu modificarile de rigoare), iar elementele din asemenea vectori sunt folositi la fel (din nou, cu modificarile de rigoare). Atentie: Indiferent de numarul de dimensiuni al vectorului, indiferent de numarul de elemente pe care le contine, aveti grija sa nu incercati sa accesati pozitii care nu exista (cum ar fi pozitia 1001 sau 2000 in vectorul cu 1000 de elemente). In cel mai bun caz, valoarea citita de acolo va fi aleatoare. In cel mai rau caz veti primi un AV (access violation pentru necunoscatori) sau vreo eroare de rulare (runtime error). Matrici patratice Matricile patratice sunt acei vectori bidimensionali a caror numar de linii este egal cu numarul de coloane. La acest tip de matrice putem vedea niste lucruri interesante, cum ar fi faptul ca numarul de elemente este egal cu latura2 (clar, nu?) ... sau ca are diagonale. Prin faptul ca are diagonale ma refer ca exista o serie de elemente ale matricii care determina respectivele diagonale. Diagonalele unei matrici patratice sunt denumite: diagonala principala si diagonala secundara. Diagonala principala porneste din coltul din stanga sus si se termina in coltul din dreapta jos. Diagonala secundara incepe in coltul din dreapta sus si se termina in coltul din stanga jos. In urmatoarea imagine puteti vedea rezultatul unui program care identifica elementele celor doua diagonale ale unei matrici patratice de 8x8. Diagonala principala este afisata cu rosu, iar cea secundara cu galben.
verificati formulele Hai sa vedem cum aflam elementele din sectiuni demarcate de diagonale. Aici, cu albastru, elementele de deasupra diagonalei principale.
Codul necesar:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
30. end.
Dupa cum se vede, elementele de deasupra diagonalei principale au proprietatea ca indexul coloanei (j) este mai mare decat indexul liniei (i). Simplu. Tot cu albastru, elementele de sub cele doua diagonale:
Proprietatea elementelor cu albastru este ca indicele liniei este mai mare decat indicele liniei SI este sub diagonala secundara (adica indexul de linie este mai mare decat numarul de linii, din care scadem indexul coloanei si adunam 1). Restul sectiunilor si subsectiunilor determinate de diagonale le puteti afla si singuri, modificand programele de mai sus.
Proceduri
Sa vedem cum arata o structura generala a unei proceduri:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
De remarcat ca, spre deosebire de structura unui program, end-ul de la sfarsit este finalizat cu ; (punct si virgula), nu cu . (punct). Lista de parametri este optionala, motiv pentru care este incadrata intre paranteze drepte. Ce inseamna parametri va fi explicat putin mai tarziu. Sa vedem un program obisnuit folosind proceduri. Programul original (fara proceduri) ar fi urmatorul:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
i := 1; {*} while (i <= N) do {citim elementele} begin Write('Dati elementul ', i, ': '); ReadLn(vector[i]); i := i + 1; end; i := 1; {*} while (i <= N) do {impartim fiecare element la 2 si salvam catul} begin vector[i] := vector[i] div 2; i := i + 1; end; Write('Vectorul modificat este : '); i := 1; {*} while (i <= N) do {afisam noile valori din vector} begin Write(vector[i] : 5); i := i + 1; end;
14. 15. 16. 17. 18. 19. 21. 23. 24. 25. 26. 27. 28.
20. 22.
29.
Vedeti secventele marcate cu {*} ? Aceeasi instructiune, repetata de 3 ori... Bineinteles, intr-un program mai mare si mai stufos, secventele care se repeta contin mai multe instructiuni ... si probabil se repeta de mai multe ori. Sa vedem cum rescriem programul folosind o procedura care sa faca acelasi lucru (sa initializeze variabila icu 1).
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
4. 5. 6. 7.
8. 9. begin 10. Write('Dati N: '); ReadLn(n); {N = numarul de elemente} 11. 12. Init; {*} 13. while (i <= N) do {citim elementele} 14. begin
Write('Dati elementul ', i, ': '); ReadLn(vector[i]); i := i + 1; end; Init; {*} while (i <= N) do {impartim fiecare element la 2 si salvam catul} begin vector[i] := vector[i] div 2; i := i + 1; end; Write('Vectorul modificat este : '); Init; {*} while (i <= N) do {afisam noile valori din vector} begin Write(vector[i] : 5); i := i + 1; end;
20. 21. 22. 23. 24. 25. 27. 29. 30. 31. 32. 33. 34.
26. 28.
35.
Probabil ca acum va ganditi "Ce mare branza a rezolvat ? Numarul de linii este acum mai mare !". Da, asa e, dar asa cum am mentionat, acest program este doar un exemplu simplist. In acest caz, variabila i este o variabila globala. Nu e declarata in niciun subprogram, ci direct in programul principal. Procedura Init o poate folosi fiindca variabila a fost declarata inaintea procedurii (daca erau declarate invers, compilatorul ar fi dat eroare spunand ca variabila folosita in procedura nu poate fi gasita). Variabilele declarate in interiorul unui subprogram se numesc variabile locale. Atentie: Variabilele globale si locale pot avea acelasi nume, lucru care poate duce la probleme in program. Sa vedem un alt program care foloseste o procedura pentru a calcula factorialul unui numar (pentru cei care nu stiu, factorialul unui numar N este produsul lui 1 x 2 x ... x N).
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. var i, n, f: integer;
2.
3. 4. 5. 7.
6.
8.
9.
f := f * i; end;
10. 11.begin 12. Write('Dati N: '); ReadLn(n); 13. 14. Fact; 15. Write('Factorial de N este egal cu : ', f); 16. 17. ReadLn; {asteptam sa fie apasat Enter} 18. end.
Functii
Functiile sunt foarte asemanatoare procedurilor, singura diferenta fiind faptul ca functiile pot intoarce un rezultat. Sa vedem cum arata o structura generala a unei functii:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
Haideti sa vedem cum ar putea arata un program care calculeaza factorialul unui numar, folosind o functie de data asta:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. var i, n, f: integer;
2.
3. 4. 5. 6. 8.
function Fact: Longint; var lFact: Longint; {variabila locala} begin lFact := 1; for i := 1 to n do lFact := lFact * i;
7. 9. 10.
11. 12.
13. 14.begin 15. Write('Dati N: '); ReadLn(n); 16. 17. F := Fact; 18. Write('Factorial de N este egal cu : ', f); 19. 20. ReadLn; {asteptam sa fie apasat Enter} 21. end.
De data asta am asignat variabila F din afara subprogramului, si i-am dat valoarea rezultata din functie. Rezultatul functiei a fost setat asignand valoarea respectiva numelui functiei, in interiorul functiei. Alta modificare este faptul ca folosim o variabila locala pentru a calcula factorialul. Acest lucru nu este strict necesar, l-am facut doar pentru a exemplifica rolul variabilelor locale.
Parametri
Sa vedem ce sunt parametrii si cum ii folosim in subprograme. Lista de parametri este scrisa, practic, ca o lista de variabile. Parametrii pot fi scrisi in orice ordine sau pot fi grupati dupa tipul variabilelor. Parametrii pot fi de doua tipuri, prin modul in care sunt folositi: parametri prin valoare - acesti parametri pot fi modificati in interiorul subprogramului, dar modificarile nu sunt vizibile in exteriorul subprogramului. parametri prin adresa - acesti parametri sunt, de fapt, adresele de memorie ale unor variabile deja declarate. Modificarile aduse unor astfel de parametri in interiorul unui subprogram vor fi vizibile in exterior.
1. var i, n, f: integer;
2.
3.
function Fact(X: Integer): Longint; {X prin valoare} 4. var lFact: Longint; {variabila locala} 5. begin 6. lFact := 1; 7. 8. for i := 1 to X do 9. lFact := lFact * i;
este
un
parametru
transmis
10.
14. 15.begin 16. Write('Dati N: '); ReadLn(n); 17. 18. F := Fact(N); 19. WriteLn('Factorial de N este egal cu : ', f); 20. WriteLn('Noua valoare a lui N este : ', n); 21. 22. ReadLn; {asteptam sa fie apasat Enter} 23. end. Bun. Am calculat din nou factorialul, dar, de data aceasta, functia noastra Fact a primit un parametru prin valoare, pe care-l foloseste sa calculeze factorialul. Pe scurt, o data intrati in corpul functiei (care este apelata ca Fact(N), X este egal cu N. Ce vreau sa remarcati este faptul ca, desi am dat o noua valoare lui X in interiorul functiei, aceasta nu este transmisa inapoi lui N, dovada fiind faptul ca noua valoare a lui N (care este afisata) este aceeasi cu cea introdusa de la tastatura. Din punctul de vedere al functiei si al metodei de lucru, admitand ca valoarea lui N era egala cu 5, puteam apela Fact(5). Numele generic al parametrului ("prin valoare") este dat tocmai din acest motiv: indiferent ca functia este apelata cu o constanta sau o variabila, doar valoarea ei este transmisa. Sa vedem acelasi cod, singura diferenta fiind ca acum folosim un parametru prin adresa.
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. var i, n, f: integer;
2.
3.
function Fact(var X: Integer): Longint; {X este un parametru transmis prin adresa} 4. var lFact: Longint; {variabila locala} 5. begin 6. lFact := 1; 7. 8. for i := 1 to X do 9. lFact := lFact * i; 10. 11. X := -1; 12. Fact := lFact; {am asignat rezultatul in numele functiei !} 13. end; 14. 15.begin 16. Write('Dati N: '); ReadLn(n); 17. 18. F := Fact(N); 19. WriteLn('Factorial de N este egal cu : ', f); 20. WriteLn('Noua valoare a lui N este : ', n); 21.
Faptul ca parametrul este transmis prin adresa este simbolizat de cuvantul var care-l precede. Programul afiseaza noua valoare a lui N, asa cum era de asteptat, egala cu -1, dovada ca variabila N a fost modificata in functie. Diferenta principala intre parametrii transmisi prin valoare si cei transmisi prin adresa este faptul ca, in cazul celor transmisi prin valoare, variabila trimisa ca parametru este copiata intr-o zona de memorie temporara si trimisa functiei, ca si copie, in timp ce in cazul celor transmisi prin adresa variabila trimisa ajunge in functie neschimbata, ca original. Din acest motiv, transmiterea parametrilor prin adresa poate rapidiza executia programului (totusi, sa nu va asteptati la minuni vorbim de milisecunde si chiar mai putin). Atentie: Un subprogram poate avea o lista de parametri micsti: o parte dintre ei sa fie trimisi prin valoare, iar altii prin adresa. Tot ce conteaza este modul de declarare al listei. De remarcat ca parametrii listati in headerul subprogramului se numesc parametri formali (X este un parametru formal in programul de mai sus), care sunt inlocuiti de parametri efectivi, in momentul apelului subprogramului (N este un parametru efectiv in programul de mai sus). Atentie: Numarul, tipul si ordinea parametrilor efectivi trebuie sa corespunda cu numarul, tipul si ordinea parametrilor formali !
Vizibilitatea variabilelor
Dupa cum am explicat mai sus, se pot declara variabile in interiorul subprogramelor, chiar si cu nume de variabile care exista deja definite in programul principal. Problema care se ridica este urmatoarea : de unde stie compilatorul (si implicit, programatorul) care variabila va fi folosita in anumite zone ale programului ? Nu trebuie sa va speriati, este foarte simplu si usor de inteles. Variabilele globale ale programului sunt vizibile oriunde in program. Daca un subprogram isi defineste o variabila cu acelasi nume, atunci variabila locala e prioritara in acel subprogram (si in posibilele subprograme ale subprogramului !). Sa vedem un exemplu.
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
3.
function Fact(X: Integer): Longint; {X prin valoare} 4. var lFact: Longint; {variabila locala} 5. begin
este
un
parametru,
transmis
6. 7.
8.
= ', lFact);
9.
10. 11.
15. 16.begin 17. Write('Dati N: '); ReadLn(n); 18. 19. lFact := 157; 20. WriteLn('lFact global = ', lFact); 21. 22. F := Fact(N); 23. WriteLn('Factorial de N este egal cu : ', f); 24. 25. ReadLn; {asteptam sa fie apasat Enter} 26. end.
1. 2. 3. 4.
Dati N: 5 lFact global = 157 lFact local = 1 Factorial de N este egal cu : 120
Recursivitate
Un lucru important de stiut, legat de subprograme, este ca ele sunt necesare daca vrem sa folosimrecursivitate. Un subprogram este recursiv daca in implementarea lui se apeleaza pe el insusi. Recursivitatea poate fi un concept destul de greu de inteles, asa ca vom incepe cu un exemplu. Sa vedem cum rescriem functia care calculeaza factorialul, folosind recursivitatea.
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
Dupa se poate vedea, recursivitatea permite si formularea mai eleganta a solutiei. Sa incercam sa intelegem ce se intampla. Factorial de N (notat si N!) este definit ca o inmultire succesiva a numerelor de la 1 la N (1 x 2 x ... x N). Din asta putem vedea ca N! este egal cu (N-1)! x N. La randul lui, (N-1)! = (N-2)! x (N-1) si asta poate continua, pana cand N este egal cu 1. Daca incercam sa calculam 1! folosind aceeasi relatie (adica 1! = 0! x 1) o sa cam dam gres, fiindca 0, inmultit cu orice numar, da 0. Pe scurt, daca avem un caz initial caruia ii stim rezultatul, iar restul cazurilor se pot rezolva in functie de cazul initial, problema poate fi rezolvata recursiv. Cazul initial, pe functia de mai sus, este cazul in care X nu este mai mare decat 1 (adica este mai mic sau egal !), acest caz avand rezultatul 1. Pentru orice alta valoare pozitiva a lui X, rezolvarea se face in functie de X-1, pana cand X ajunge in cazul initial. Ca urmare, aceste doua relatii de mai sus: N! = (N-1)! x N 1! = 1 sunt tot ce ne trebuie pentru a ne defini functia recursiva de calculare a factorialului. Sa vedem ce se intampla, pas cu pas, pentru N egal cu 3.
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. 2. 3. 4. 5.
Fact(3) X = 3 -> apelam Fact(2) si inmultim cu 3 X = 2 -> apelam Fact(1) si inmultim cu 2 X = 1 -> 1, se termina functia, revenim in apelul precedent Fact = 1, care inmultit cu 2 este egal cu 2, revenim in apelul precedent 6. Fact = 2, care inmultit cu 3 este egal cu 6, se termina functia si revenim in locul unde a fost initial apelata functia.
Deci, daca avem o problema care poate fi exprimata prin subprobleme ale ei si putem gasi o conditie de terminare a apelurilor recursive (in acest caz, X = 1), problema poate fi rezolvata printr-un subprogram recursiv. Atentie: Apelurile recursive consuma relativ multa memorie. Daca o problema poate fi rezolvata iterativ (fara apeluri recursive), e de preferat (in general) ca problema sa fie rezolvata iterativ. O lista prea lunga de apeluri recursive poate genera "stack overflow" si bloca programul / calculatorul. Pentru edificare, atasez acelasi program care calculeaza factorialul, modificat pentru a afisa ce se intampla. Sper sa va fie de ajutor, impreuna cu explicatiile de mai sus. Codul sursa al programului:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. uses crt;
2.
3. const lin = 9; 4.
line = '-----------------------------------------------------------------'; 5. Wait = 1000; 6. 7. var i, n, f, lFact, test: longint; 8. 9. function Fact(X: Integer): Longint; {X este un parametru, transmis prin valoare} 10. begin 11. if X > 1 12. then 13. begin 14. WriteLn('Fact(', X, ') = Fact(', X - 1 ,') * ', X); 15. WriteLn(line); 16. Delay(Wait); 17. 18. Fact := Fact(X - 1) * X; 19. Delay(Wait); 20. 21. TextColor(LightGreen); 22. if Test > 0 then 23. Test := Test * X; 24. GotoXY(40, (N - X) * 2 + lin); 25. Write('Fact(', X,') = ', Test); 26. end 27. else 28. begin 29. TextColor(LightRed); 30. WriteLn('X = 1 ' + 31. 'Fact(1) = 1, caz initial !'); 32. TextColor(LightGray); 33. WriteLn(line); 34. Delay(Wait); 35. WriteLn('Functia s-a terminat, revenim din apel ^^^'); 36. Delay(Wait); 37. Fact := 1; 38. Test := 1; 39. end; 40. end; 41. 42.begin 43. test := 0; 44. repeat {verificam daca 1 <= N <= 6} 45. ClrScr; 46. Write('Dati N (maxim 6): '); ReadLn(n); 47. until (N > 0) and (N < 7); 48. ClrScr; 49. 50. TextColor(LightRed);
55. 56. 57. 59. 60. 61. 63. 64. 65. 67. 68. 69. 70. 71. 73. 74.
GotoXY(Length(line) div 2 - 16 ,2); WriteLn('Calculul factorialului - recursiv'); WriteLn(line); WriteLn; TextColor(LightGray); WriteLn('Se apeleaza Fact(', N,') ...'); Delay(Wait); TextColor(lightGray); GotoXY(1, lin); F := Fact(N); Delay(Wait); GotoXY(1, N * 2 + lin + 3); TextColor(LightGray); Write('Fact(', N, ') este egal cu : '); TextColor(LightGreen); WriteLn(f); TextColor(LightGray); Write('... apasa orice tasta pentru a termina programul ...');
58.
62.
66.
72. While Keypressed Do Readkey; {daca s-a apasat o tasta in timp ce programul rula, 75. va astepta totusi apasarea urmatoarei taste} 76. ReadKey; 77. end. Surse si executabil - Download Screenshot din timpul rularii programului:
Lucrul cu fisiere
Toate programele facute pana acum acceptau, ca sursa de date, tastatura, iar ca iesire, monitorul. Cu toate acestea, Pascal permite folosirea fisierelor. Sunt convins ca v-ati intrebat, cel putin o data, de ce jocurile se instaleaza cu o multime de fisiere aditionale, in afara de executabilul propriu-zis. Raspunsul e simplu: resursele respectivului joc se afla in acele fisiere (harti, imagini, sunete, etc). Executabilul jocului citeste si scrie acele fisiere in functie de necesitati (scrie, de exemplu, in fisierul de high-scores). Sa vedem cum putem folosi si noi fisiere, in Pascal. Pentru inceput trebuie sa stiti ca Pascal ofera trei tipuri de fisiere: fisiere text fisiere cu tip fisiere fara tip Modul de lucru (accesare, citire, scriere) difera, cel putin partial, la fiecare tip de fisier. Sa incepem cu inceputul.
1. 2 5 2. 1 2 3 4 5 3. 6 7 8 9 10
Sa vedem un program care citeste acest fisier si afiseaza datele citite pe monitor.
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. uses crt; 2. var fis: Text; 3. lins, cols, i, j: integer; 4. matrice: array[1 .. 20, 1 .. 20] of integer;
5. begin 6. ClrScr; 7. Assign(fis, 'fisier.txt'); 8. Reset(fis); 9. 10. ReadLn(fis, lins, cols); 11. for i := 1 to lins do 12. begin
WriteLn('Matricea citita din fisier este:'#13#10); for i := 1 to lins do {#13#10 = Enter} begin {#13 = Carriage Return} for j := 1 to cols do {#10 = Line Feed} write(matrice[i,j] : 3); 24. writeln; 25. end; 26. Write(#13#10'Apasati orice tasta pentru a iesi ...'#8#8#8#8); 27. ReadKey; {#8 = BackSpace} 28. end. Declaratia unui fisier text se face identic cu declararea unei variabile obisnuite, iar tipul variabilei esteText. Fisier Text . Restul variabilelor nu are rost sa le explic, le puteti intelege daca ati parcurs tutorialele precedente. Sa vedem ce proceduri si functii avem la dispozitie pentru lucrul cu fisiere text.
Nume Assign
Descriere Asigneaza un fisier (definit prin calea catre el) catre o variabila de tip fisier. Toate operatiile ulterioare care folosesc acel fisier vor utiliza variabila acestuia. Asocierea dintre variabila si respectivul fisier va exista pana la urmatoarea asignare a variabilei catre alt fisier. Calea catre fisier trebuie sa fie o cale definita prin 0 sau mai multe directoare, separate prin semnul backslash \, urmate de numele complet al fisierului. Daca aceasta cale este inceputa direct cu semnul backslash, atunci este folosita ca si cum ar fi inceput direct in radacina ( C:\Folder\fisier.txt este identic cu\Folder\fisier.txt, daca programul este rulat dintr-un director de pe C:. Daca este dat doar numele fisierului, atunci fisierul este cautat in directorul curent (cel de unde este rulat programul). Numele fisierului trebuie sa se conformeze standardului DOS de denumire a fisierelor, adica 8.3 (nume de maxim 8 caractere, fara spatii, si extensie (optionala) de maxim 3 caractere. Un caz special este cel in care calea catre fisier este sirul vid (adica '' doua apostroafe, unul dupa celalalt). In acest caz, variabila de fisier este asignata intrarii / iesirii standard. Dupa o asignare a variabilei catre sirul
vid, folosirea proceduriiReset va asocia variabila catre intrarea standard, iar folosirea procedurii ReWrite va asocia aceasta variabila cu iesirea standard. Restrictie: procedura Assign nu poate fi folosita pe un fisier deja deschis. Deschide un fisier existent pentru citire. Daca fisierul nu exista, aceasta procedura va genera o eroare. Reset Dupa ce fisierul a fost deschis, pozitia in fisier (file pointer-ul) este setata la inceputul acestuia (de acolo poate incepe citirea). Daca variabila de fisier a fost asignata unui sir vid, dupa apelul acestei proceduri, variabila de fisier se va referi la intrarea standard (tastatura, de obicei). Creaza un fisier nou si il deschide pentru scriere. Daca fisierul exista, atunci el este sters si recreat (gol). Daca variabila de fisier este asignata unui fisier deschis, acesta va inchis, sters, recreat si redeschis. Rewrite Dupa ce fisierul a fost deschis, pozitia in fisier (file pointer) este setata la inceputul acestuia. Daca variabila de fisier a fost asignata unui sir vid, dupa apelul acestei proceduri, variabila de fisier se va referi la iesirea standard (monitorul, de obicei). Deschide fisierul ales (prin folosirea procedurii Assign) pentru scriere. Daca fisierul nu exista, atunci aceasta procedura va genera o eroare. Daca fisierul este deja deschis, atunci el este inchis si redeschis. Pozitia in fisier (file pointer) este setata la sfarsitul acestuia. Daca variabila de fisier a fost asignata unui sir vid, dupa apelul acestei proceduri, variabila de fisier se va referi la iesirea standard (monitorul, de obicei). Inchide fisierul desemnat de variabila de fisier, daca respectivul fisier a fost deschis folosind una din procedurile Reset, Rewrite sau Append. Close Fisierul asociat cu variabila de fisier este adus la zi cu toate modificarile operate asupra lui, inainte de a fi inchis. EoLN Aceasta functie (acronim de la End of LiNe) returneaza True daca pozitia in fisier (file pointer-ul) este la sfarsitul unei linii (marcaj #13#10) sau la
Append
sfarsitul fisierului. Returneaza False in caz contrar. Functia poate fi apelata fara parametru sau cu un parametru de tip Text (fisier text). Daca este apelata fara parametru, atunci fisierul este considerat a fi fisierul standard de intrare (tastatura). Verifica daca mai sunt date pe linia curenta, de la pozitia curenta pana la sfarsitul liniei. Daca nu mai sunt date pana la sfarsitul liniei (doar spatii sau tab-uri), va returna True, returnand False in caz contrar. SeekEoLN Restrictii: Fisierul trebuie sa fie deschis. Poate fi folosita doar cu fisiere text. Aceasta functie (acronim de la End of File) returneaza True daca pozitia in fisier (file pointer) este la sfarsitul fisierului. Returneaza False in caz contrar. EoF Functia poate fi apelata fara parametru sau cu un parametru de tip Text (fisier text). Daca este apelata fara parametru, atunci fisierul este considerat a fi fisierul standard de intrare (tastatura). Verifica daca mai sunt date in fisier, de la pozitia curent pana la sfarsitul fisierului. Daca nu mai sunt date pana la sfarsitul fisierului (doar spatii sau tab-uri), va returnaTrue, returnand False in caz contrar. SeekEoF Restrictii: Fisierul trebuie sa fie deschis. Poate fi folosita doar cu fisiere text.
Daca ati trecut prin explicatiile date in tabelul de mai sus, ar trebui sa stiti destul de multe pentru a folosi un fisier text fara probleme. Sa vedem un mic program care citeste prima linie dintr-un fisier text si o adauga, inversata, la sfarsitul fisierului.
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
10.
14. WriteLn('Vom adauga linia, inversata, in fisier, la sfarsitul acestuia.'); 16. WriteLn; 17. 18. for i := 1 to Length(linie) div 2 do {bucla aceasta inverseaza sirul de caractere} 19. begin 20. c := linie[i]; 21. linie[i] := linie[Length(linie) - i + 1]; 22. linie[Length(linie) - i + 1] := c; 23. end; 24. 25. Append(fis); 26. WriteLn(fis, #13#10 + linie); 27. Close(fis); 28. Write(#13#10'S-a facut ! Apasati orice tasta pentru a iesi ...'); 29. ReadKey; 30. end. Ultimul program care va folosi fisiere text in acest tutorial va citi date dintr-un fisier si le va salva in alt fisier.
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
23.
24. end.
ReadKey;
Atentie: presupun ca este evident ca toate fisierele de intrare folosite in aceste programe vor trebui create manual inainte de pornirea programului.
16.
Assign(fis, 'fisier.bin'); ReSet(fis); Seek(fis, 19); {indexul incepe de la 0} Read(fis, i); WriteLn('A 20-a valoare din fisier este : ',i); Close(fis);
20.
23. 25. Write(#13#10'S-a facut ! Apasati orice tasta pentru a iesi ...'); ReadKey; 28. end. 27. Este de remarcat faptul ca fiecare element din fisier ocupa 2 bytes, in acest caz (integer). Lista de proceduri si functii care pot fi folosite la manipularea fisierelor cu tip sunt date in tabelul de mai jos. Explicatiile date in acest tabel sunt doar complementare celor date in tabelul de proceduri si functii dat pentru folosirea cu fisiere text.
Descriere Deschide un fisier existent (pentru citire / scriere). Daca fisierul nu exista, aceasta procedura va genera o eroare.
Reset
Dupa ce fisierul a fost deschis, pozitia in fisier (file pointer-ul) este setata la inceputul acestuia. Daca variabila de fisier a fost asignata unui sir vid, dupa apelul acestei proceduri, variabila de fisier se va referi la intrarea standard (tastatura, de obicei). Creaza un fisier nou si il deschide (pentru scriere / citire). Daca fisierul exista, atunci el este sters si recreat (gol). Daca variabila de fisier este asignata unui fisier deschis, acesta va inchis, sters, recreat si redeschis.
Rewrite
Dupa ce fisierul a fost deschis, pozitia in fisier (file pointer) este setata la inceputul acestuia. Daca variabila de fisier a fost asignata unui sir vid, dupa apelul acestei proceduri, variabila de fisier se va referi la iesirea standard (monitorul, de obicei).
Close
Inchide fisierul desemnat de variabila de fisier, daca respectivul fisier a fost deschis folosind una din procedurile Reset sau Rewrite.
Fisierul asociat cu variabila de fisier este adus la zi cu toate modificarile operate asupra lui, inainte de a fi inchis. Returneaza numarul de componente din fisierul desemna de variabila de fisier. Marimea actuala a fisierului poate fi aflata inmultind rezultatul acestei functii cu numarul de bytes folositi de fiecare componenta. FileSize Restrictii: Fisierul trebuie sa fie deschis. Nu poate fi folosita cu fisiere text. Returneaza pozitia in fisier (file pointer-ul). Daca pozitia este chiar la inceputul fisierului, atunci aceasta functie returneaza 0. Daca pozitia este la sfarsitul fisierului, atunci rezultatul lui FilePos este egal cu cel al functie FileSize, prezentata mai sus. Restrictii: Fisierul trebuie sa fie deschis. Nu poate fi folosita cu fisiere text. Muta pozitia in fisier (file pointer) la cea specificata. Urmatoarea citire sau scriere se face din acel loc. Numarul primei componente din fisier este 0. Noua pozitie care poate fi specificata este de tip Longint, deci nu poate merge mai departe de 2 GB. Pentru a scrie la sfarsitul fisierului (dupa ultima componenta) se poate folosi cu impreuna cu rezultatul functiei FileSize. Seek Daca pozitia este chiar la inceputul fisierului, atunci aceasta functie returneaza 0. Daca pozitia este la sfarsitul fisierului, atunci rezultatul lui FilePos este egal cu cel al functie FileSize, prezentata mai sus. Restrictii: Fisierul trebuie sa fie deschis. Nu poate fi folosita cu fisiere text. Trunchiaza fisierul de la pozitia curenta in fisier (file pointer). Toate componentele de dupa pozitia actuala in fisier sunt sterse. Truncate Restrictii: Fisierul trebuie sa fie deschis. Nu poate fi folosita cu fisiere text.
FilePos
Niciodata nu mi-a placut restrictia care spunea ca aceasta procedura / functie nu poate fi folosita cu fisiere text, asa ca am cautat o solutie. Solutia evidenta este sa folositi un fisier cu componente de tipChar (practic, text), putand astfel folosi si functii gen FileSize, FilePos, Seek, Truncate. Totusi, probabil ca ati vrea sa aveti grija la sfarsiturile de linie / fisier
Descriere In cazul fisierelor fara tip, aceasta procedura accepta si un parametru de tip Word, care specifica numarul de bytes avut de o componenta din fisier (RecSize). In mod implicit, RecSize este egal cu 128. Este preferabil sa il setati pe 1, avand astfel control absolut asupra citirilor / scrierilor facute in fisier. Formatului apelului acestei proceduri este urmatorul:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
Reset
Rewrite
In cazul fisierelor fara tip, aceasta procedura accepta si un parametru de tip Word, care specifica numarul de bytes avut de o componenta din fisier (RecSize). In mod implicit, RecSize este egal cu 128. Este preferabil sa il setati pe 1, avand astfel control absolut asupra citirilor / scrierilor facute in fisier. Formatului apelului acestei proceduri este urmatorul:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
Identic cu fisierele cu tip. Identic cu fisierele cu tip. Identic cu fisierele cu tip. Identic cu fisierele cu tip.
Truncate
Identic cu fisierele cu tip. Citeste din fisierul selectat un numar specificat de bytes, intr-o variabila declarata de utilizator. Formatul apelului este urmatorul:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
Count: Word; [;var Result: Word]); F este un fisier fara tip, Buffer poate fi orice variabila, Count este un numar intreg, iar Result este o variabila intreaga. Aceasta functie citeste un numar de Count (sau mai putin) componente (marimea unei componente este specificata de Reset si Rewrite) din fisierul F in memorie, in variabila Buffer. Numarul de componente citite BlockRead este returnat in variabila Result(daca este folosita). Daca variabila Result nu este folosita, o eroare I/O este generata in cazul in care numarul de componente citite este diferit de numarul specificat in apel (Count). Daca Result este egala cu Count, atunci operatia a avut efectul dorit. Numarul de bytes cititi este, in cazul optim, Count * RecSize (RecSize este 128 de bytes, daca nu este specificat altfel). Daca numarul Count * RecSize este mai mare decat 65535 (64 KB), se va genera o eroare. Pozitia in fisier este mutata la sfarsitul zonei citite, ca efect al instructiunii BlockRead. Restrictii: Fisierul trebuie sa fie deschis. Nu poate fi folosita decat cu fisiere fara tip. BlockWrite Scrie in fisierul selectat un numar specificat de bytes, dintr-o variabila declarata de utilizator. Formatul apelului este urmatorul:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
F este un fisier fara tip, Buffer poate fi orice variabila, Count este un numar intreg, iar Result este o variabila intreaga.
Aceasta functie scrie un numar de Count (sau mai putin) componente (marimea unei componente este specificata de Reset si Rewrite) in fisierul F din memorie, din variabila Buffer. Numarul de componente scrise este returnat in variabilaResult (daca este folosita). Daca variabila Result nu este folosita, o eroare I/O este generata in cazul in care numarul de componente scrise este diferit de numarul specificat in apel (Count). Daca Result este egala cu Count, atunci operatia a avut efectul dorit. Numarul de bytes scrisi este, in cazul optim, Count * RecSize (RecSize este 128 de bytes, daca nu este specificat altfel). Daca numarul Count * RecSize este mai mare decat 65535 (64 KB), se va genera o eroare. Pozitia in fisier este mutata la sfarsitul zonei scrise, ca efect al instructiunii BlockWrite. Restrictii: Fisierul trebuie sa fie deschis. Nu poate fi folosita decat cu fisiere fara tip.
In continuare, un exemplu de program care copiaza un fisier (fara niciun fel de verificari).
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. uses crt; 2. var src, dest: file; 3. buffer: array[1 .. 32768] of byte; {buffer de 32 KB} 4. cale_src, cale_dest: string; 5. read_bytes: word;
6. begin 7. ClrScr; 8. Write('Dati calea catre fisierul sursa: '); 9. ReadLn(cale_src); 10. 11. Write('Dati calea destinatie: '); 12. ReadLn(cale_dest); 13. 14. Assign(src, cale_src); 15. Reset(src, 1); 16. 17. Assign(dest, cale_dest); 18. Rewrite(dest, 1); 19. 20. While not Eof(src) Do {atat timp cat nu am ajuns la sfarsitul fisierului ...}
21. 22.
Begin BlockRead(src, buffer, 32768, read_bytes); {vrem sa citim 32 KB, dar verificam cat am citit} 23. BlockWrite(dest, buffer, read_bytes); {cat am citit, atat scriem} 24. End; 25. Close(src); 26. Close(dest); 27. WriteLn(#13#10'Fisierul a fost copiat !'); 28. ReadKey; 29. end.
Daca fisierul care se doreste copiat nu exista, sau daca fisierul destinatie nu are extensie (lol ) sau intervine orice alta problema, programul va returna erori. Nu este dat decat pentru a exemplifica un mod de lucru cu fisierele fara tip.
Creat si testat doar pe Borland Pascal 7.0, deci sa nu va aud Aici e codul:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. uses crt; 2. const spaced = ' '; 3. select = ' * '; 4. var c: char; 5. sel: integer;
6. begin 7. clrscr; 8. c := #0; 9. sel := 1; 10. {afisam meniul initial} 11. TextColor(Yellow); 12. TextBackground(Blue); {galben pe albastru pentru optiunea selectata} 13. WriteLn(select, '1. BitCell.info'); 14. 15. TextColor(LightGray); 16. TextBackground(Black); 17. WriteLn(spaced, '2. a doua optiune'); {gri deschis pe negru pentru restul} 18. WriteLn(spaced, '3. a treia optiune'); 19. WriteLn(spaced, '4. a patra optiune');
'5. a cincea optiune'); '6. a sasea optiune'); '7. a saptea optiune'); 'Press Esc to Exit ...');
25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 46. 47. 48. 49. 50. 51. 52. 53. 54. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68.
44. 45.
repeat c := ReadKey; {asteptam o tasta} case c of #0: {daca e tasta cu cod extins} begin c := ReadKey; {citim codul extins al tastei} case c of #72: {0 - 72 = codul tastei sageata sus} begin if sel > 1 then {nu putem selecta 0 sau mai mic} sel := sel - 1; end; #80: {0 - 80 = codul tastei sageata jos} begin if sel < 7 then {nu putem selecta mai mult de 7} sel := sel + 1; end; else writeln('0 - ', ord(c)); end; clrscr; WriteLn(spaced, WriteLn(spaced, WriteLn(spaced, WriteLn(spaced, WriteLn(spaced, WriteLn(spaced, WriteLn(spaced, WriteLn(#13#10, GotoXY(1, sel); '1. BitCell.info'); '2. a doua optiune'); '3. a treia optiune'); '4. a patra optiune'); '5. a cincea optiune'); '6. a sasea optiune'); '7. a saptea optiune'); 'Press Esc to Exit ...');
55. TextColor(Yellow); TextBackground(Blue); case sel of 1: WriteLn(select, '1. 2: WriteLn(select, '2. 3: WriteLn(select, '3. 4: WriteLn(select, '4. 5: WriteLn(select, '5. 6: WriteLn(select, '6. 7: WriteLn(select, '7. end; TextColor(LightGray); TextBackground(Black);
BitCell.info'); a doua optiune'); a treia optiune'); a patra optiune'); a cincea optiune'); a sasea optiune'); a saptea optiune');
69. 70. {
end; else WriteLn(ord(c));} {comentat pentru a nu afisa codul tastei apasate} 71. end; 72. 73. until (c = #27); {caracterul cu codul ASCII 27 corespunde tastei Esc} 74. {deci folosim tasta Esc pentru a iesi din program} 75. end.
Niste explicatii: uses crt; - este vorba de biblioteca (unit) de functii si proceduri care faciliteaza lucrul in modul text in Pascal. Daca linia asta lipseste nu veti putea folosi o parte din functiile si procedurile de prin program. ClrScr - (clear screen) sterge orice text de pe ecran si readuce culorile de afisare la valorile predefinite. TextColor - seteaza culoarea textului (in modul text). Definita in unitul CRT. TextBackGround - seteaza culoarea fondului (in modul text). Definita in unitul CRT. ReadKey - asteapta apasarea unei taste si returneaza caracterul apasat (nu "prinde" taste gen Ctrl, Shift, CapsLock, etc). Daca tasta are cod extins (tastele functionale, sagetile, etc), atunci va returna 0 si va trebui ca functia ReadKey sa fie apelata din nou pentru a primi codul extins al tastei. GotoXY - seteaza pozitia cursorului de text. Determina locul de pornire pentru urmatoarea afisare. Screenshot-uri din timpul rularii programului:
Creat si testat doar pe Borland Pascal 7.0, ati fost avertizati Aici e codul:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. Program Text_miscator; 2. uses crt; 3. var N, i, start, dif1, dif2: integer; 4. s: String; 5. linii, coloane: array[1 .. 80] of byte; 6. SuntAranjate: Boolean;
7. begin 8. ClrScr; 9. S := 'BitCell.info - IT forum - programare si algoritmica, hardware si altele'; 10. N := Length(S); {Length returneaza lungimea sirului de caractere} 11. 12. Randomize; {initializeaza variabila interna RandSeed cu o valoare luata din ora sistemului} 13. {aceasta variabila este variabila de la care porneste generatorul de numere aleatoare} 14.
for i := 1 to n do begin linii[i] := Random(49) + 1; {pozitii random, dar fara ultima linie / coloana} 18. coloane[i] := Random(79) + 1; 19. gotoxy(coloane[i], linii[i]); 20. write(s[i]); 21. end; 22. 23. start := 40 - (N div 2); 24. 25. repeat 26. SuntAranjate := True; 27. for i := 1 to N do 28. begin 29. if (linii[i] <> 12) or (coloane[i] <> start + i - 1) then 30. begin 31. {calculam noua pozitie} 32. dif1 := abs(coloane[i] - (start + i - 1)); {abs = functia modul din matematica} 33. dif2 := abs(linii[i] - 12); 34. 35. if ((dif1 = 0) and (dif2 = 0)) or (s[i] = ' ') 36. then Continue {continuam bucla FOR, cu urmatoarea valoare a lui i} 37. else 38. begin 39. GotoXY(coloane[i], linii[i]); {stergem litera din vechea pozitie} 40. TextColor(Black); 41. Write(s[i]); 42. end; 43. SuntAranjate := False; 44. 45. if dif2 > dif1 46. then 47. begin 48. if linii[i] > 12 49. then Dec(linii[i]) 50. else Inc(linii[i]); 51. end 52. else 53. begin 54. if coloane[i] > (start + i - 1) 55. then Dec(coloane[i]) 56. else Inc(coloane[i]); 57. end; 58. 59. {reafisam litera in noua pozitie}
end; end; Delay(50); {acest interval de asteptare poate fi modificat pentru viteza} 67. until SuntAranjate; 68. 69. GotoXY(start, 12); {pe ecran, in modul text, sunt 80 de coloane} 70. Write(S); {asa ca afisam textul de la pozitia 40 minus jumatate din text} 71. ReadLn; 72. end.
Niste explicatii: Randomize - pregateste generatorul de numere random (aleatoare) Random - genereaza un numar random, intre 0 si N - 1. abs - returneaza valoarea absoluta a unui numar. abs(5) = abs(-5) = 5 Inc - creste valoarea variabilei folosite ca parametru cu 1 sau o valoare specificata. Dec - scade valoarea variabilei folosite ca parametru cu 1 sau o valoare specificata. Delay - opreste (pauzeaza) executia programului pentru un numar specificat de milisecunde. Screenshot-uri din timpul rularii programului:
Creat si testat doar pe Borland Pascal 7.0, deci sa nu va aud Urmeaza codul:
LINE NUMBER ON/OFF | EXPAND/CONTRACT | SELECT ALL
1. uses crt, graph; 2. type star = record 3. X, Y, Z: Integer; 4. { pozitia si departarea "stelei" } 5. end;
6. 8.
7. const N = 250; {numarul de "stele"} 9. var gd, gm: Integer; {init graphics mode} 10. i, Sx, Sy: integer; 11. stars: array[1 .. N] of star;
12.
13.
procedure InitStars; {initializeaza "N" stele : pozitie si departare} 14. var i: Integer; 15. begin 16. Sx := GetMaxX; {numarul de coloane in modul grafic} 17. Sy := GetMaxY; {numarul de linii in modul grafic} 18. 19. Randomize; 20. for i := 1 to N do 21. begin 22. stars[i].X := Random(Sx - 9) + 5; {pozitia} 23. stars[i].Y := Random(Sy - 9) + 5; 24. stars[i].Z := Random(250) + 1; {departarea trebuie sa fie} 25. end; {cat mai aleatorie la inceput} 26. end; 27. 28. procedure MoveStars; 29. var i, X, Y, Z: Integer; 30. color, speed: Byte; 31. pause: Boolean;
begin pause := False; speed := 5; repeat {bucla infinita ... este oprita daca se apasa tasta Escape} if not pause then for i := 1 to N do begin X := round((stars[i].X * 256) / stars[i].Z) + Sx shr 1; {calcularea pozitiei unei stele, in functie de Z (adancime)} 40. Y := round((stars[i].Y * 256) / stars[i].Z) + Sy shr 1; {shr 1 = div 2. Shift pe biti catre dreapta, adica impartire intreaga la 2} 41. SetColor(Black); 42. Rectangle(X, Y, X+1, Y+2); 43. 44. if (X < 2) or (X > Sx - 2) or {daca "steaua" iese din monitor,} 45. (Y < 2) or (Y > Sy - 2) or {ii schimbam pozitia si departarea} 46. (stars[i].Z < 10) then 47. begin 48. stars[i].X := Random(Sx - 9) + 5; 49. stars[i].Y := Random(Sy - 9) + 5; 50. stars[i].Z := Random(20) + 230; {departarea trebuie sa fie maxima} 51. end; 52. Dec(stars[i].Z); 53. 54. X := Round((stars[i].X * 256) / stars[i].Z) + Sx shr 1;{noua coloana a "stelei"} 55. Y := Round((stars[i].Y * 256) / stars[i].Z) + Sy shr 1;{noua linie a "stelei"} 56. 57. case (stars[i].Z div 3) of {cu cat "steaua" e mai departata,} 58. 65: color := DarkGray; {cu atat e mai inchisa la culoare} 59. 35: color := LightGray; 60. 20: color := White; 61. end; 62. 63. SetColor(color); 64. if stars[i].Z > 120 65. then PutPixel(X, Y, color) {cu cat e mai aproape steaua,} 66. else Rectangle(X, Y, X+1, Y+1) {cu atat e mai mare} 67. end; 68. 69. if KeyPressed then 70. begin 71. case UpCase(ReadKey) of
72. 73.
iesim}
#27 : Break;
'P' : pause := not pause; {s-a apasat P, punem pauza sau o scoatem} 74. '+' : if (speed > 1) then {plus - crestem viteza} 75. dec(speed); 76. '-' : if (speed < 80) then {minus - scadem viteza} 77. inc(speed); 78. end; 79. end; 80. 81. Delay(speed); 82. until False; 83. end; 84. 85.begin 86. gd := 0; 87. InitGraph(gd, gm, ''); 88. 89. InitStars; 90. MoveStars; 91. 92. CloseGraph; 93. end.
Functiile si procedurile legate de afisare le voi explica intr-un tutorial separat. Screenshot din timpul rularii programului: