A. Considerații teoretice

Implementarea produselor software necesită deseori, pentru a satisface cerințele unui anumit set de date, structuri de date mai flexibile decât înregistrările cu număr fix de componente. În astfel de situații este convenabil să putem folosi înregistrări (articole) care să aibă o parte comună și o parte a cărei structură să poată să fie diferită de la o înregistrare la alta.

Să considerăm, de exemplu, că dorim să calculăm perimetrul unei figuri geometrice care poate fi: triunghi, dreptunghi sau cerc. În toate cele trei cazuri trebuie să reținem valoarea perimetrului dar, în plus, pentru păstrarea lungimilor laturilor, respectiv a razei, sunt necesare trei câmpuri la triunghi, două la dreptunghi și unul la cerc.

Un alt exemplu ar fi o listă de persoane care conține următoarele informații despre fiecare persoană: numele, data nașterii, starea civilă (necăsătorit, căsătorit, divorțat). În plus, pentru persoanele căsătorite se mai înregistrează numele soțului/soției și numărul de copii, iar pentru cele divorțate, data divorțului.

Articolele care permit mai multe variante de structuri de câmpuri se numesc articole cu variante.

Pentru exemplele considerate anterior putem defini următoarele tipuri:

type
  Fig = (Cerc, Triunghi, Dreptunghi);
  Contur = record
    Perimetru: real;
  case Fel: Fig of  
    Triunghi: (Latura1, Latura2, Latura3: integer);   
    Cerc: (Raza: integer);                       
    Dreptunghi: (Lungime, Latime: integer)
  end;    {Contur}

și respectiv:

const MaxPersoane = 50;
type
  IndexPersoane = 1..MaxPersoane;
  TipStareCivila = (Necasatorit, Casatorit, Divortat);
  Data = record
    Zi: 1..31;
    Luna: 1..12;
    An: 1900..3000;
  end;     {Data}
  Persoana = record
    Nume: string[20];
    DataNasterii: Data;
  Case StareCivila: TipStareCivila of
    Necasatorit: ();
    Casatorit: (NrCopii: integer; NumeSot: string[20]);
    Divortat: (DataDivort: Data)
  end;    {Persoana}
  ListaPersoane = array[IndexPersoane] of Persoana;

În legătură cu sintaxa înregistrărilor cu variante, există patru aspecte importante care trebuie reținute:

  1. Câmpul selector poate fi de orice tip ordinal; în mod frecvent (ca și în exemplele anterioare) el va fi de tipul enumerare.

  2. Sintaxa articolelor cu variante permite prezența unei singure clauze case; pot exista însă clauze case imbricate (o clauză case în cadrul altei clauze case), dar asemenea cazuri sunt rare.

  3. Fiecare valoare a tipului selector trebuie sa apară ca și etichetă a unei variante; daca varianta este vida (nu are nici un câmp) forma ei este: constanta ( ).

  4. Partea variantă trebuie sa fie întotdeauna ultima în structura unui articol; nici o definiție de câmp nu mai poate să apară dupa ea. Un singur cuvant cheie end marchează atât sfârșitul clauzei case cât și a structurii record.

În ceea ce privește declararea unor variabile de tipul articol cu variante, accesul la componente și operațiile permise, rămân valabile toate aspectele studiate la tipul articol. Prin urmare, putem scrie următoarele declarații:

var Figura: Contur;

pentru primul exemplu, și:

var Lista: ListaPersoane;

pentru cel de-al doilea exemplu.

După declararea unei variabile de tipul articol cu variante, câmpul selector nu are asignată nici o valoare și este lipsită de sens referirea, în această situație, la partea variantă. O soluție ar fi atribuirea, mai întâi, a unei anumite valori câmpului selector și apoi referirea părții variante, ca în secvențele următoare:

Figura.Fel := Cerc;
Figura.Raza := 4;

sau

Figura.Fel := Dreptunghi;
Figura.Lungime := 6;
Figura.Latime := 3;

Procedând astfel, ne vom referi doar la câmpuri care prezintă interes pentru o anumită problemă. Selectarea operațiilor corespunzătoare unei anumite variante se face, de obicei, în conjuncție cu instrucțiunea case. Folosirea instrucțiunii case la prelucrarea articolelor cu variante va fi exemplificată și în programele prezentate în secțiunea exemple.

Trebuie reținut că o secvență de forma:

Figura.Fel := Cerc;
Figura.Lungime := 10;

... nu va fi semnalată ca eroare de compilare și programul va intra in execuție. Prevenirea unor astfel de erori logice cade în responsabilitatea programatorului.


B. Exemple

  1. Să se scrie un program care calculează perimetrul unei figuri geometrice: triunghi, dreptunghi sau cerc, cunoscând dimensiunile laturilor, respectiv raza.

Program PerimetruContur;

Type
  Fig = (Cerc, Triunghi, Dreptunghi);
  Contur = record
    Perimetru: real;
  Case Fel: Fig of 
    Triunghi: (Latura1, Latura2, Latura3: integer);
    Cerc: (Raza: integer);
    Dreptunghi: (Lungime, Latime: integer);
  End;   {Contur}
  
Var
  Figura: Contur;
  Raspuns: char;

function Converteste(c: char): fig;
begin   {converteste}
  case c of 
    'T', 't': Converteste := Triunghi;
    'C', 'c': Converteste := Cerc;
    'D', 'd': Converteste := Dreptunghi;
  end;   {case}
end;   {Converteste}

begin   {programul principal}
  repeat
    repeat
      write('Introduceti tipul figurii (T/D/C): ');
      readln(Raspuns);
    until Raspuns in ['T', 't', 'D', 'd', 'C', 'c'];
    Figura.Fel := Converteste(Raspuns);
    with Figura do 
      case Fel of 
        Triunghi:
        begin   {triunghi}
          write('Introduceti lungimile laturilor: ');
          readln(Latura1, Latura2, Latura3);
          perimetru := Latura1 + Latura2 + Latura3;
          writeln('Perimetru= ',Perimetru:6:2);
        end;  {Triunghi}
        Cerc:
        begin    {cerc}
          write('Introduceti raza: ');
          readln(Raza);
          perimetru := 2 * pi * Raza;
          writeln('Perimetru= ', Perimetru:6:2);
        end;   {Cerc} 
        Dreptunghi: 
        begin     {Dreptunghi}
          write('Introduceti lungimea: ');
          readln(Lungime);
          write('Introduceti latimea: ');
          readln(Latime);
          perimetru := 2 * (Lungime + Latime);
          writeln('Perimetru= ', Perimetru:6:2);
        end;    {Dreptunghi}
      end;   {case}
    write('Continuati? (N pentru terminare): ');
    readln(Raspuns);
  until (Raspuns = 'N') or (raspuns = 'n');
end.

Click aici pentru sursa completă (ex01_1.pas)

  1. Să se scrie un program care citește datele corespunzătoare unei liste de persoane: nume, data nașterii, starea civila (necăsătorit, căsătorit, divorțat), numele soțului și numărul de copii pentru persoanele căsătorite și data divorțului pentru cele divorțate. Programul va efectua următoarele prelucrări:

    • Ordonează lista în ordinea alfabetică a numelor;

    • afișează numele și numărul de copii ale persoanelor care au mai mult de doi copii.

program StareCivila;

const MaxPersoane = 50;

type 
  IndexPersoane = 1..MaxPersoane;
  TipStareCivila = (necasatorit, casatorit, divortat);
  Data = record
    zi: 1..31;
    luna: 1..12;
    an: 1900..3000;
  end;  {data}
  Persoana = record
    Nume: string[20];
    DataNasterii: data;
  case StareCivila: TipStareCivila of
    necasatorit: ();
    casatorit: (nrCopii: integer; numeSot: string[20]);
    divortat: (DataDivort: data);
  end;  {persoana}
  ListaPersoane = array[IndexPersoane] of Persoana;

var 
  Lista: ListaPersoane;
  NrPersoane: IndexPersoane;

function Transforma(c: char): TipStareCivila;
{stabileste starea civila a unei persoane}
begin
  case c of 
    'n', 'N': transforma := necasatorit;
    'c', 'C': transforma := casatorit;
    'd', 'D': transforma := divortat;
  end;  {case}
end;  {Transforma}

procedure CitesteData(var d: data);
{citeste o data calendaristica exprimata in zi,luna,an}
begin
  with d do
  begin
    write('   zi:   ');
    readln(zi);
    write('   luna: ');
    readln(luna);
    write('   an:   ');
    readln(an)   		
  end;{with}
end;  {CitesteData}

procedure CitesteLista(var N: IndexPersoane; var L: ListaPersoane);
{citeste numarul de persoane din lista si informatiile asociate lor}
var
  I: IndexPersoane;
  Raspuns: char;
begin
  repeat
    write('Introduceti numarul de persoane (1-', MaxPersoane, '): ');
    readln(N);
  until (N >=1 ) and (N <= MaxPersoane);
  writeln('Introduceti lista de persoane');
  for I := 1 to N do
  begin
    writeln('    Persoana ', I:3);
    with L[I] do 
    begin
      write('Numele: ');
      readln(Nume);
      writeln('Data nasterii: ');
      CitesteData(DataNasterii);
      repeat
        write('Starea civila (N/C/D): ');
        readln(Raspuns);
      until Raspuns in ['N', 'n', 'C', 'c', 'D', 'd'];
      StareCivila := Transforma(Raspuns);
      case StareCivila of 
        Casatorit:
        begin
          write('Numele sotului/sotiei: ');
          readln(NumeSot);
          write('Numar copii: ');
          readln(NrCopii);
        end; {Casatorit}
        Divortat:
        begin
          writeln('Data divortului: ');
          CitesteData(DataDivort)
        end; {Divortat}
      end;{case}
    end;{with}
  end;{for}
end; {CitesteLista}

procedure Interschimba(var P1, P2: Persoana);
{interschimba continutul a doua variabile de tipul Persoana}
var Auxiliar: Persoana;
begin {Interschimba}
  Auxiliar := P1;
  P1 := P2;
  P2 := Auxiliar;
end;{interschimba}
   
Procedure Ordoneaza(N: IndexPersoane; var L: ListaPersoane);
{ordoneaza alfabetic lista de persoane}
var
  I: IndexPersoane;
  Ordonat: boolean;
begin {Ordoneaza}
  repeat 
    Ordonat := true;
    for I := 1 to N-1 do
      if L[I].Nume > l[I+1].Nume then 
      begin
        Interschimba(L[I], L[I+1]); 
        Ordonat := false;
      end;
  until Ordonat;
end; {Ordoneaza}

procedure Afiseaza(N: IndexPersoane; L: ListaPersoane);
{afiseaza numele si nr-ul de copii pt pers cu mai multi copii}
var I: IndexPersoane;
begin
  for I := 1 to N do
    if L[I].StareCivila = Casatorit then 
      if L[I].NrCopii > 2 then 
        writeln(L[I].Nume:20, L[I].NrCopii:3);
end; {afiseaza}

begin{progr principal}
  CitesteLista(NrPersoane, Lista);
  Ordoneaza(NrPersoane, Lista);
  Afiseaza(NrPersoane, Lista);
end.

Click aici pentru sursa completă (ex01_2.pas)


C. Teme

  1. Să se realizeze programul Pascal care execută următoarele operații:

    • Citește de pe mediul de intrare numele unui utilizator, care poate fi un student sau un cadru didactic. Dacă este student se va citi numărul său matricol, iar dacă este cadru didactic se va citi gradul universitar (asistent, lector, conferențiar, profesor). Pentru toți utilizatorii se va citi timpul lucrat pe calculator (zile, ore, minute și secunde);

    • Afișează un tabel de forma celui ilustrat mai jos:

Nr

Nume

Grad Universitar

Timp lucrat

1

Popescu Doru

profesor

13h22min34sec

2

Manea Dorin

asistent

16h20min02sec

3

Alexa Ion

student/0987

20h45min56sec

4

George Marius

student/7865

24h00min00sec

Timp total lucrat

03z02h18min32sec

  1. Să se scrie un program care înregistrează publicațiile: reviste și carti, dintr-o bibliotecă. Pentru fiecare publicație se precizează titlul, domeniul, prețul și dacă este sau nu împrumutată. În plus, pentru cărți se mai precizează: autorul și numărul de pagini, iar pentru reviste anul și luna apariției. Programul trebuie să permită realizarea următoarelor operații:

    • Introducerea unei publicații în bibliotecă;

    • Afișarea publicațiilor aparținând unui anumit domeniu, precizând dacă sunt sau nu împrumutate;

    • Afișarea datelor despre o publicație pentru care se precizează titlul;

    • Marcarea împrumutului unei carți;

    • Marcarea înapoierii unei carți;