Tablice, rekordy i wskaźniki Tablice i rekordy Wskaźniki
Tablice A: array (Integer range 1.. 6) of Float; for I in 1.. 6 loop A(I) := 0.0; end loop; Tablice mogą być więcej niż jednowymiarowe: AA: array (Integer range 0.. 2, Integer range 0.. 3) of Float; for I in 0.. 2 loop for J in 0.. 3 loop A(I, J) := 0.0; end loop; end loop;
Dyskretny zakres nie musi byś statyczny: N: Integer := ; B: array (Integer range 1.. N) of Boolean; Indeks może być podtypem dowolnego typu dyskretnego: Hours_Worked: array (Day) of Float; for D in Weekday loop Hours_Worked(D) := 8.0; end loop; Hours_Worked(Sat) := 0.0; Hours_Worked(Sun) := 0.0; Tylko dla dni roboczych: Hours_Worked: array (Day range Mon.. Fri) of Float; Hours_Worked: array (Weekday) of Float; -- better
Tablice mają różne atrybuty związane z ich indeksami: Hours_Worked'First = Mon Hours_Worked'Last = Fri Hours_Worked'Length = 5 -- number of values of the first (or only) index A Range jest skrótem A First.. A Last: Hours_Worked Range is Mon.. Fri Te same atrybuty można zastosować do różnych wymiarów tablicy: AA'First(1) = 0 AA'Last(1) = 2 AA'Length(1) = 3 AA'First(2) = 0 AA'Last(2) = 3 AA'Length(2) = 4 AA'Range(1) is 0.. 2 AA'Range(2) is 0.. 3
Atrybut Range można użyć w deklaracji: J: Integer range A'Range; J: Integer range 1.. 6; Inicjowanie tablic: A: array (1.. 6) of Float := (0.0, 0.0, 0.0, 0.0, 0.0, 0.0); AA: array (0.. 2, 0.. 3) of Float := ((0.0, 0.0, 0.0, 0.0), (0.0, 0.0, 0.0. 0.0), (0.0, 0.0, 0.0)); Stałe tablice: Work_Day: constant array (Day) of Boolean := (True, True, True, True, True, False, False); Tomorrow: constant array (Day) of Day := (Tue, Wed, Thu, Fri, Sat, Sun, Mon);
for I in A'Range loop A(I) := A(I) + 1.0; end loop; for E of A loop E := E + 1.0; end loop; for E of AA loop E := 0.0; end loop; of zamiast in
Typ tablicowy type Vector_6 is array (1.. 6) of Float; A: Vector_6; B: Vector_6; B := A; -- B(1) := A(1); B(2) := A(2); ; B(6) := A(6) C: array (1.. 6) of Float; D: array (1.. 6) of Float; D := C; -- illegal E, F: array (1.. 6) of Float; F := E; -- illegal
type Vector is array (Integer range <>) of Float; subtype Vector_5 is Vector(1.. 5); V: Vector_5; type Matrix is array (Integer range <>, Integer range <>) of Float; subtype Matrix_3 is Matrix(1.. 3, 1.. 3); M: Matrix_3; M: Matrix(1.. N, 1.. N); -- bounds could be any expressions type Vector is array (Integer range <>) of Float; type Row is array (Integer range <>) of Float; V: Vector(1.. 5); R: Row(0.. 4); R := Row(V); -- is valid type Vector is array (Integer range <>) of Float with Default_Component_Value => 99.999; subtype Vec is Vector nowe w Ada 2012 with Default_Component_Value => 0.0;
Agregaty tablicowe Unit_2: constant Matrix := ( 1 => (1 => 1.0, 2 => 0.0), 2 => (1 => 0.0, 2 => 1.0)); A: array (1.. 6) of Float := (1.. 6 => 0.0); Work_Day: constant array (Day) of Boolean := (Mon.. Fri => True, Sat Sun => False); A: array (1.. N) of Integer := (A'Range => 0); -- illegal A: array (1.. N) of Integer := (others => 0); A: array (1.. 1) of Integer := (99); -- illegal (99) = 99 A: array (1.. 1) of Integer := (1 => 99);
Znaki i łańcuchy Literał znakowy składa się z pojedynczego znaku ujętego w apostrofy. type Roman_Digit is ('I', 'V', 'X', 'L', 'C', 'D', 'M'); Dig: Roman_Digit := 'D'; Roman_Digit'First = 'I' Roman_Digit'Succ('X') = 'L' Roman_Digit'Pos('M') = 6 Dig < 'L' = False Dostępne są predefiniowane typy Character, Wide_Character (16-bit) i Wide_Wide_Character (32-bit). 'X' < 'L' -- is ambiguous
Character'('X') < 'L' -- False Roman_Digit'('X') < 'L' -- True Jest również predefiniowany typ dla łańcuchów: type String is array (Positive range <>) of Character; S: String(1.. 7); G: constant String := ('P', 'I', 'G'); -- bounds are 1 and 3 Łańcuch znaków ujęty w cudzysłów nie jest zarezerwowany jedynie dla predefiniowanego typu String: type Roman_Number is array (Positive range <>) of Roman_Digit; Nineteen_Eighty_Four: constant Roman_Number := "MCMLXXXIV"; Four: array (1.. 2) of Roman_Digit := "IV";
Tablice tablic i plasterki type Vector_6 is array (1.. 6) of Float; type Vector is array (Integer range <>) of Float; type Matrix_3_6 is array (1.. 3) of Vector_6; type Matrix_3_N is array (1.. 3) of Vector; -- illegal type Matrix_N_6 is array (Integer range <>) of Vector_6; AOA: Matrix_3_6; -- or AOA: Matrix_N_6(1.. 3); MDA: Matrix(1.. 3, 1.. 6); -- multidimensional array Agregaty dla AOA i MDA są identyczne: ((1.0, 2.0, 3.0, 4.0, 5.0, 6.0), (4.0, 4.0, 4.0, 4.0, 4.0, 4.0), (6.0, 5.0, 4.0, 3.0, 2.0, 1.0)) ale odwołania do elementów zupełnie inne: AOA(I)(J) i MDA(I, J).
type String_Array is array (Positive range <>, Positive range <>) of Character; Farmyard: constant String_Array := ("pig", "cat", "dog", "cow", "rat", "hen"); Zoo: constant String_Array := ("aardvark", "baboon", "camel", ); -- illegal Zoo: constant String_Array := ("aardvark", "baboon ", "camel ", ); -- ok Put(Farmyard(5)); -- illegal type String_3_Array is array (Positive range <>) of String(1.. 3); Farmyard: constant String_3_Array := ("pig", "cat", "dog", "cow", "rat", "hen"); Put(Farmyard(5)); -- ok Plasterek tablicy: S: String(1.. 10); S(3.. 8) -- the middle six characters of S T: constant String := S(3.. 8); -- T'First = 3, T'Last = 8 S(1.. 4) := "BARA"; S(4.. 7) := S(1.. 4); -- results in S(1.. 7) = "BARBARA" (not "BARBARB") Pets: String_3_Array(1.. 2) := Farmyard(2.. 3); -- "cat" and "dog" Farmyard(1)(1.. 2) := "ho"; -- turns the "pig" into a "hog"
Operacje na tablicach jednowymiarowych type Bit_Row is array (Positive range <>) of Boolean; A, B: Bit_Row(1.. 4); C, D: array (1.. 4) of Boolean; T: constant Boolean := True; F: constant Boolean := False; A := (T, T, F, F); B := (T, F, T, F); A := A and B; -- A equals (T, F, F, F) B := not B; -- B equals (F, T, F, T) C := C and D; -- illegal
type Primary is (R, Y, B); type Colour is array (Primary) of Boolean; White: constant Colour := (F, F, F); Red: constant Colour := (T, F, F); Yellow: constant Colour := (F, T, F); Blue: constant Colour := (F, F, T); Green: constant Colour := (F, T, T); Purple: constant Colour := (T, F, T); Orange: constant Colour := (T, T, F); Black: constant Colour := (T, T, T); Red or Yellow = Orange not Black = White "CAT" < "DOG" -- illegal "CCL" < "CCXC" -- illegal Wide_String'("CAT") < "DOG" -- True String'("CCL") < "CCXC" -- True Roman_Number'("CCL") < "CCXC" -- False
Nineteen_Eighty_Four < "MM" -- True -- arrays of any discrete type: (1, 2, 3) < (2, 3) -- True (Jan, Jan) < (1 => Feb) Operator & łączy dwie jednowymiarowe tablice (konkatenacja): "CAT" & "ERPILLAR" = "CATERPILLAR" "This string goes " & "on and on" Jednym lub oboma argumentami & może być pojedyncza wartość tego typu co element tablicy: "CAT" & 'S' = "CATS" 'S' & "CAT" = "SCAT" 'S' & 'S' = "SS"
"First line" & Latin_1.CR & Latin_1.LF & "Next line" CRLF: constant String := (Latin_1.CR, Latin_1.LF); "First line" & CRLF & "Next line" R: Roman_Number(1.. 5); S: String(1.. 5); R := "CCL" & "IV"; -- ok S := "CCL" & "IV"; -- ok B: Boolean := "CCL" < "IV"; -- illegal
Rekordy type Month_Name is (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec); type Date is record Day: Integer range 1.. 31; Month: Month_Name; Year: Integer; end record; D: Date; D.Day := 4; D.Month := Jul; D.Year := 1776; D: Date := (4, Jul, 1776); E: Date; E := D; E := (Month => Jul, Day => 4, Year => 1776);
type Complex is record Re, Im: Float := 0.0; end record; C1: Complex; -- equals (0.0, 0.0) C2: Complex := (1.0, 0.0); C3: Complex := (Re => 1.0, Im => <>); -- C3.Im is default value I: constant Complex := (0.0, 1.0); -- root of -1.0 type Hole is null record; type Subject is (Theology, Classics, Mathematics); type Score is array (Subject) of Integer range 0.. 100; type Student is record Birth: Date; Finals: Scores := (others => 0); end record; Fred: Student; Fred.Birth := (19, Aug, 1984); Fred.Finals := (5, 15, 99); Fred := (Birth => (19, Aug, 1984), Finals => (5, 15, 99)); People: array (1.. N) of Student; People(6).Birth.Day := 19; People(8).Finals(Classics.. Mathematics) := (50, 50);
Playing with pointers is like playing with fire John Barnes. Programming in Ada 2012 Wskaźniki type Cell; type Cell_Ptr is access Cell; type Cell is record Next: Cell_Ptr; Value: Integer; end record; L: Cell_Ptr; L := new Cell; L: null
L := new Cell'(null, 37); N: Cell_Ptr; N := new Cell'(L, 10); L := N; L: null L: null 37 37 N: null N: 10 (a) N: Cell_Ptr; (b) N := new Cell (L, 10); L: null 37 N: 10 (c) L := N;
L := N kopiuje wskaźnik a nie obiekt. Jeśli chcemy skopiować obiekt: L.Next := N.Next; L.Value := N.Value; albo z użyciem all: L.all := N.all; L = N zachodzi gdy L i N wskazują ten sam obiekt a L.all = N.all zachodzi gdy dwa wskazywane obiekty mają takie same wartości. C: constant Cell_Ptr := new Cell'(null, 0); C.all := L.all; -- ok C := L; -- illegal
Nie potrzebujemy zmiennej N aby wydłużyć listę: L := new Cell'(L, 10) procedure Add_To_List(List: in out Cell_Ptr; V: in Integer) is begin List := new Cell'(List, V); end Add_To_List; Niezainicjowane zmienne wskaźnikowe przyjmują zawsze domyślną wartość null. function Sum(List: Cell_Ptr) return Integer is Local: Cell_Ptr := List; S: Integer := 0; begin while Local /= null loop S := S + Local.Value; Local := Local.Next; end loop; return S; end Sum;
type Node; type Node_Ptr is access Node; type Node is record Left, Right: Node_Ptr; Value: Float; end record; procedure Sort(A: in out Vector) is Index: Integer; Tree: Node_Ptr := null; procedure Insert(T: in out Node_Ptr; V: in Float) is begin if T = null then T := new Node'(null, null, V); elsif V < T.Value then Insert(T.Left, V); else Insert(T.Right, V); end if; end Insert; verte
procedure Output(T: in Node_Ptr) is begin if T /= null then Output(T.Left); A(Index) := T.Value; Index := Index + 1; Output(T.Right); end if; end Output; begin -- body of Sort for I in A'Range loop Insert(Tree, A(I)); end loop; Index := A'First; Output(Tree); end Sort;
type Ref_Int is access Integer; R: Ref_Int := new Integer'(46); R.all := 13; type A_String is access String; A: A_String := new String'("Hello"); function "+"(S: String) return A_String is begin return new String'(S); end "+"; type A_String_Array is array (Positive range <>) of A_String; Zoo: constant A_String_Array := (+"aardvark", +"baboon",, +"very long animal",, +"zebra"); Wyrażenia w agregacie są obliczane dla każdego indeksu: A: array (1.. 10) of Cell_Ptr := (others => new Cell); Tablica A zawiera dziesięć wskaźników do dziesięciu różnych rekordów.
A, B: Cell_Ptr := new Cell; -- creates two new cells A: Cell_Ptr := new Cell; B: Cell_Ptr := A; L: Cell_Ptr := new Cell'(N.all); K: Integer := 46; R: Ref_Int := new Integer'(K); Wykluczanie null i ograniczenia: type Cell_Ptr is not null access Cell; type Cell_Ptr is access Cell; subtype NN_Cell_Ptr is not null Cell_Ptr; M: NN_Cell_Ptr := L; -- checks not null M: not null Cell_Ptr := L;
procedure P(X: in out not null Ref_Int); -- legal procedure Q(X: in out Integer range 1.. N); -- illegal function F(X: Integer) return not null Ref_Int; -- legal type Ref_Pos is access Positive; type Ref_Pos is access Integer range 1.. Integer'Last; RP: Ref_Pos := new Positive'(10); RP: Ref_Pos := new Integer'(10); type Ref_Matrix is access Matrix; R: Ref_Matrix; R := new Matrix'(1.. 5 => (1.. 10 => 0.0)); -- qualified expression R := new Matrix(1.. 5, 1.. 10); -- subtype
Aliasy Fraza aliased: type Int_Ptr is access all Integer; IP: Int_Ptr; I: aliased Integer; IP := I'Access; IP.all := 25; 1. Ostrzega, że obiekt może być przetwarzany pośrednio. 2. Informuje kompilator aby nie przechowywać obiektu w sposób niestandardowy (np. w rejestrze).
type Const_Int_Ptr is access constant Integer; CIP: Const_Int_Ptr; I: aliased Integer; C: aliased constant Integer := 1815; CIP := I'Access; -- access to a variable CIP := C'Access; -- access to a constant IP := C'Access; -- illegal CIP := Const_Int_Ptr(IP); -- legal IP := Int_Ptr(CIP); -- illegal AI: array (1.. 100) of aliased Integer; IP := AI(I)'Access;
type Ref_Count is access constant Integer 0.. 1; type Ref_Count_Array is array (Integer range <>) of Ref_Count; type Cell is record Life_Count: aliased Integer range 0.. 1; Total_Neighbour_Count: Integer range 0.. 8; Neighbour_Count: Ref_Count_Array(1.. 8); end record; This_Cell.Neighbour_Count(1) := Cell_To_The_North.Life_Count'Access; C.Total_Neighbour_Count := 0; for I in C'Neighbour_Count'Range loop C.Total_Neighbour_Count := C.Total_Neighbour_Count + C.Neighbour_Count(I).all; end loop;
Dostępność procedure Main is type AI is access all Integer; Ref1: AI; begin declare Ref2: AI; I: aliased Integer; begin -- access to a variable I with a lesser lifetime than the access type AI Ref2 := I'Access; -- illegal Ref1 := Ref2; end; declare -- some other variables begin Ref1.all := 0; end; end Main;
Parametry wskaźnikowe procedure Main is type T is procedure P is type A is access all T; Ptr: A := X'Access; -- illegal, X not in scope begin end P; begin declare X: aliased T; begin P; end; end Main; procedure Main is type T is procedure P(Ptr: access T) is begin end P; begin declare X: aliased T; begin P(X'Access); end; end Main;
procedure Main is type T is type A is access all T; Ref: A; procedure P(Ptr: access T) is begin Ref := A(Ptr); -- dynamic check on conversion end P; X: aliased T; begin P(X'Access); can now manipulate X via Ref end Main; W powyższym przykładzie przypisanie Ref := A(Ptr) jest bezpieczne bo czas życia X nie jest krótszy niż typu A. Ale nie musi być bezpieczne we wszystkich wywołaniach P, więc musi być sprawdzane dynamicznie i gdy zawodzi zgłaszany jest wyjątek Program_Error.
Anonimowy typ wskaźnikowy
Wskaźniki do podprogramów function Sin(X: Float) return Float; type Math_Function is access function (F: Float) return Float; Do_It: Math_Function; X, Theta: Float; Do_It := Sin'Access; X := Do_It(Theta); -- abbreviation for X := Do_It.all(Theta)
procedure Iterate(Func: in not null Math_Function; V: in out Vector) is begin for I in V'Range loop V(I) := Func(V(I)); end loop; end Iterate; A_Vector : Vector := (100.0, 4.0, 0.0, 25.0); Iterate(Sqrt'Access, A_Vector); -- A_Vector is now (10.0, 2.0, 0.0, 5.0)
type Converter is not null access procedure (S: in out String); procedure Encrypt(Text: in out String); procedure Decrypt(Text: in out String); procedure Apply(Proc: in Converter; To: in out String) is begin Proc(To); -- indirect call end Apply; Sample: String := "Send reinforcements. We're going to advance."; Apply(Proc => Encrypt'Access, To => Sample);
type Integrand is not null access function (X: Float) return Float; function Integrate(Fn: Integrand; Lo, Hi: Float) return Float; Area := Integrate(Log'Access, 1.0, 2.0); type Action is access procedure; Action_Sequence: array (1.. N) of Action; -- build the array for I in Action_Sequence'Range loop Action_Sequence(I).all; -- need for.all because are no parameters end loop;
procedure Main is function F(X: Float) return Float is begin return Exp(X**2); end F; Result, L, H: Float; begin -- set bounds in L and H Result := Integrate(F'Access, L, H) -- illegal -- subprogram F has a lifetime less than that of -- the access type Integrand end Main; Konieczne jest użycie anonimowego typu wskaźnikowego: function Integrate( Fn: not null access function (X: Float) return Float; Lo, Hi: Float) return Float;
Pula pamięci
Niesprawdzana dealokacja with Ada.Unchecked_Deallocation; type Cell_Ptr is access Cell; procedure Free is new Ada.Unchecked_Deallocation(Cell, Cell_Ptr); List: Cell_Ptr; Free(List); Wartością zmiennej List będzie null a wcześniej wskazywana komórka wróci do puli wolnej pamięci.