Grundsätzliches

Techniken des imperativen Programmierens:  Verfahren wird durch Folge von Anweisungen beschrieben; Daten werden in strukturierten Behältern (Variablen) abgelegt, zu jedem Behälter gibt ein Datentyp an, was hineingelegt werden darf; Zuweisung als grundlegende Anweisung; Festlegung der Bearbeitungsreihenfolge durch Kontrollstrukturen; Zusammenfassung von Teilverfahren zu Prozeduren.

Variablen und Zuweisung:  Variable als Behälter, zu jedem Behälter gehört ein Kontrollmechanismus, der den Datentyp des zugewiesenen Werts prüft;
elementare Datentypen: Integer, Natural, Float, Char sowie Boolean.

Diszipliniertes Vorgehen

Vorgehen beim „Programmieren im Kleinen“:  Ideen umgangssprachlich aufschreiben; präzise Formulierung; Ermitteln von Eigenschaften; Entwicklung eines Algorithmus; Implementierung in ein Programm; Testen & Messen; kritisches Überdenken.

Programmaufbau in Ada

Programmauf bau: 

<with/use-Bereich>
procedure <Name> is
  <Deklarationsteil>
begin
  <Anweisungsteil>
end <Name>;

ist ein Platzhalter, Kommentare durch - bis zum Zeilenende, Name besteht aus mindestens einem Bezeichner (Folge von Buchstaben, Ziffern und Unterstrichen, die mit einem Buchstaben beginnt), keine Unterscheidung der Groß-/Kleinschreibung, Schlüsselwörter dürfen nicht als Name verwendet werden (Datentypen aber schon)

Deklarationsteil:

  enthält Variablen- ( : ;) sowie
Konstantendeklarationen ( : constant := ;), bei Integer und Float darf der Datentyp weggelassen werden), zu den wichtigsten skalaren Datentypen gehören Boolean, Integer, Float sowie Character Anweisungsteil:  endliche Folge von elementaren und strukturierten Anweisungen, wie
Zuweisung ( := ;), No-Op (null;) und Ein-/Ausgabe
(Get ();, Put ();) 1.4 Funktionen und Prozeduren Funktionsdeklaration: 
function <Name> (<Parameter>)
 return <Datentyp> is
  <Deklarationsteil>
begin
  <Anweisungsteil>
end <Name>;

in der Funktion muss man auf eine elementare Anweisung der Form return ; stoßen, die Liste der formalen Parameter enthält die Parameter mit Name und Datentyp getrennt durch Semikolon

Prozedurdeklaration:


procedure <Name> (<Parameter>) is
  <Deklarationsteil>
begin
  <Anweisungsteil>
end <Name>;

die Prozedur kann vorzeitig über return; verlassen werden, wird als Anweisung verwendet, wie bei Funktion heißt der Teil bis zum is Kopf, der Rest Rumpf

Rekursion:

Rekursion ist die (in)direkte Verwendung einer Funktion in ihrem Rumpf. Operatoren:  Operatoren sind spezielle Funktionen (in der Regel ein- oder zweistellig) und besitzen statt eines Namens ein Operatorsymbol, in Ada können Operatoren wie Funktionen deklariert werden (z. B. function "+" usw.) 1.5 Skalare Datentypen Datentyp:  Eine Menge zusammen mit der auf ihr definierten Operationen heißt Datentyp, es gibt skalare (Datentypen, die man nicht auf andere zurückführt, wie vordefinierte und Aufzählungs-Datentypen) und zusammengesetzte Datentypen Aufzählungstyp:  type is ();
(der Aufzählungstyp ist geordnet, d. h. die Reihenfolge ordnet die Elemente an)
Operationen (Attribute): nullstellig (alle Elemente von T, T’First, T’Last, T’(Sa)),
einstellig (T’Pred (X), T’Succ (X), T’Pos (X), T’Val (I)) sowie
zweistellig (=, /=, <, <=, >, >=, T’Min (X, Y), T’Max (X, Y)) Standard-Datentypen:  Boolean, Character, Integer (Natural, Positive), Float Boolean:  Wertebereich False/True, Operationen not, and, or, xor, =, and then,
or else, Klammerung bei logischen Ausdrücken wird empfohlen (teilweise verpflichtend!) Character:  wird als Aufzählungstyp mit den Zeichen aus ISO-8859-1 (Latin-1) als Wertebereich aufgefasst, Zeichen werden in Apostrophe eingeschlossen (Apostroph durch zwei Apostrophe), Operationen: wie bei allen Aufzählungstypen Integer:  wird als Aufzählungstyp von Integer’First bis Integer’Last (rechnerabhängig) aufgefasst, Operationen: alle Konstanten, +, -, abs (alle einstellig), +, -, *, mod (nicht-negativ), / (ganzzahlige Division, Abschneiden der Dezimalen), rem (auch negativ), **, =, /=, <, <=, >, >=, Min, Max, Pred, Succ, Pos, Val (Ausnahme: Integer’Pos (I) = I) Float:  eigene Floattypen festlegen durch Angabe von Ober-/Untergrenze
(type is delta d range unten .. oben;) oder durch Angabe
der Dezimalziffern (type is delta p digits a;, hier ist p eine Zehnerpotenz), der eingebaute Typ Float besitzt eine Genauigkeit von mindestens 6 Dezimalziffern (eigene Genauigkeit durch type is digits a;), Operationen: +, -, abs (alle einstellig), +, -, *, /, ** (rechts darf nur eine ganze Zahl stehen), =, /=, <, <=, >, >= (Vermeidung von =, /= aufgrund Rundungsfehlern), ausnahmsweise können ganze mit reellen Zahlen multipliziert und reelle Zahlen durch ganze dividiert werden Typumwandlung:  Ada ist streng typisiert, d. h. es können meist nur zwei Operanden gleichen Typs verrechnet werden. Abhilfe schaffen die Funktionen Float (I); sowie
Integer (X); (rundet zur nächsten ganzen Zahl!). Initialisierung:  Variablen können bei der Deklaration durch
: := ; initialisiert werden.
Dies gilt auch für Aufzählungstypen, Arrays und Records. Ein-/Ausgabe:  erfolgt über die Paket Ada.Text_IO, Ada.Integer_Text_IO, und
Ada.Float_Text_IO (über with und ggf. use einbinden) 1.6 Felder Unterbereiche:  In Ada werden Unterbereiche durch subtype mittels
subtype is range .. ; deklariert.
range .. kann weggelassen werden, in diesem Fall besitzt der Unterbereich die gleiche Wertemenge wie der Datentyp. Alle Operationen des Datentyps werden vom Unterbereich übernommen. Ein Variable vom Typ eines Unterbereichs wird als Variable des Basistyps aufgefasst, erst bei der Zuweisung wird die Einschränkung geprüft.
Vorderfinierte Unterbereiche sind Natural und Positive.
Unterbreiche kann man auch für Float einführen, sofern die Grenzen reellwertig sind. Array:  Arraytypen können durch
type is array () of ; deklariert werden.
Mehrdimensionale Arrays können durch Angabe von zwei kommagetrennten Indexdatentypen deklariert werden (z. B. type Matrix is array (1 .. 10, 1 .. 50) of Float;).
Bei einem statischen Feld sind alle Feldgrenzen zur Übersetzungszeit bekannt, sonst heißt das Feld dynamisch (in Ada können die Feldgrenzen aber nicht verändert werden). Unspezifizierte Feldgrenzen:  bei Typdeklaration ist es erlaubt, die Feldgrenzen wegzulassen und stattdessen range <> zu schreiben
(z. B. type Text is array (Natural range <>) of Character;).
Die Feldgrenzen müssen jedoch bei der Variablendeklaration angegeben werden. Operationen: T’Range (I) (gibt den Felddatentyp der I-ten Dimension an, bei eindimensionalen Arrays kann (1) weggelassen werden), T’Length (I) (Anzahl der Elemente), T’First (I), T’Last (I) (erstes/letztes Element des Indexdatentyps) 1.7 BNF und EBNF BNF (Backus-Naur-Form):  Eine BNF ist ein Viertupel \((V, \Sigma , P, S)\) mit
  • \(V\) nicht-leere endliche Menge der Form <Zeichenkette> (Nichtterminalzeichen),

  • \(\Sigma \) nicht-leere endliche Menge mit \(V \cap \Sigma = \emptyset \) und \(| \notin \Sigma \) (Terminalzeichen),

  • \(P\) endliche Menge (Regeln oder Produktionen), zu jedem Nichtterminalzeichen \(A \in V\) gibt es genau eine Regel aus \(P\) mit der Form A ::= u1 | ... | uk, wobei jedes ui eine Folge von Zeichen aus \(V \cup \Sigma \) ist (die leere Zeichenfolge \(\varepsilon \) ist als ein ui zugelassen),

  • \(S \in V\) (Startsymbol).

EBNF (erweiterte BNF):  die BNF wird um folgende Möglichkeiten erweitert:

  • Verwenden von Schlüsselwörtern, die in Apostrophe eingeschlossen werden (z. B.
    <Operatoren>::= 'and' | 'or'), die dann als Terminalzeichen aufgefasst werden

  • Einführen von eckigen Klammern, deren Inhalt auch ausgelassen werden kann (z. B.
    <Ziffernfolge>::= <Ziffer>[<Ziffernfolge>])

  • Einführen von geschweiften Klammern, deren Inhalt beliebig oft wiederholt und ausgelassen werden kann (z. B. <Ziffernfolge>::= <Ziffer>{<Ziffer>})

Kontrollstrukturen

Fallunterscheidung: 

if Ausdruck1 then <Anweisungsfolge>
elsif Ausdruck2 then <Anweisungsfolge>
elsif Ausdruck3 then <Anweisungsfolge>
else <Anweisungsfolge>
end if;

Auswahlanweisung: 

case Tag is
  when Mo | Di => <Anweisungsfolge>
  when Mi .. Fr => <Anweisungsfolge>
  when others => <Anweisungsfolge>
end case;

Die Auswahlmöglichkeiten bei case müssen disjunkt und vollständig sein! Von Ada werden alle Möglichkeiten ausgewertet, die case-Anweisung wird nur dann ausgeführt, wenn genau einmal der Wert True vorkam.

while-Schleife: 

while B loop
   <Anweisungsfolge>
end loop;

for-Schleife: 

for I in 1 .. 10 loop
   <Anweisungsfolge>
end loop;

Mit reverse wird der Bereich der Laufvariablen rückwärts durchlaufen. Mit dem Befehl exit when B; wird die aktuelle Schleife verlassen, falls B zutrifft (when B kann weggelassen werden).

loop end loop; erzeugt eine Endlosschleife. Wird eine Schleife mit einem Namen versehen (z. B. Schleife1: while B loop), so kann sie mit
exit Schleife1 when B; verlassen werden (when B kann weggelassen werden).
Mittels exit dürfen allerdings keine Blöcke verlassen werden.

Block: 

<Block> ::= [<Blockname>:] [declare <Deklarationsteil>]
 begin
   <volle_Anweisungsfolge>
 end [<Blockname>];

Blöcke liefern die zentrale Grundstruktur von Programmen in Ada. Mittels declare-Blöcken können dynamische Felder erstellt werden. Alle Variablen des Blocks unterliegen Lebensdauer und Sichtbarkeit (nicht sichtbare Objekte können mittels . sichtbar gemacht werden).

Sprunganweisung:  Mittels goto abc; springt man an die Anweisung des Programms, die mit abc markiert ist (z. B. «abc» null;). Mit goto darf man nicht in eine struktuierte Anweisung oder aus einem Block heraus springen.

Records

Records: 

type Datum is record
  Jahr : Integer := 2010;
  Monat : Monatsname;
  Tag : Integer range 1 .. 31;
end record;
-- Tag, Monat und Jahr heissen Selektoren
WM_Beginn : Datum := (2010, Juni, 11);
NJ : Datum := Datum'(2011, Tag => 1, Monat => Januar);
-- Datum'/Verbundaggregat kann man weglassen

Einen leeren Verbund kann man mit type Leerer_Verbund is null record; deklarieren. Mit Verbundaggregaten kann man einen ganzen Record auf einmal befüllen (entweder Liste von Werten, Liste von Werten mit Angabe der Selektoren oder Liste der Werte, danach Liste mit Angabe der Selektoren). others am Ende befüllt die restlichen Komponenten.
Vorbesetzungen der Komponenten sind durch Verbund-Initialisierungen möglich.
Ist der Datentyp nicht eindeutig, so ist Typ-Qualifizierung (Datum’) erforderlich.

Variante Records: 

type SL is (D, EU, sonst);
type Student (Herkunft : SL) is record
  Name : Unbounded_String;
  Matrikelnummer : Positive;
  case Herkunft is
    when D => Ort : Unbounded_String;
    when EU | sonst =>
          Land : Unbounded_String;
  end case;
end record;
type Kategorie is (PKW, Bus, Karren);
type Fahrzeug (Art : Kategorie := PKW)
     is record
  Laenge, Breite, Hoehe : Float;
  case Art is
    when Bus => record Sitzpl : 8 .. 60;
     Stehpl : 0 .. 80; end record;
    when PKW => Airbags : Positive;
    when Karren => null record;
  end case;
end record;

Variante Records bekommen bei ihrer Initialisierung eine sog. Diskriminante mitgegeben, von der abhängt, welche Komponenten der Record enthält. Man spricht von einem varianten Anteil des Records. Die Diskriminante darf eine Vorbesetzung enthalten (Herkunft : SL := D). Die Auflistung im varianten Teil muss vollständig und disjunkt sein, ein Selektor darf in einem gesamten Record höchstens einmal vorkommen. Diskriminanten können auch die Größe von Records beeinflussen (z. B. übergibt man einen Integer-Wert als Diskriminante, dieser bestimmt die Größe eines Arrays).