(algebraische Datentypen;
Spezialfälle: enum, struct, class)
können einer Teilmenge ganzer Zahlen zugeordnet werden
typedef enum { 
  Mon, Tue, Wed, Thu, Fri, Sat, Sun 
} day;
Designfragen:
int umgewandelt?
int umgewandelt?
das ist nett gemeint, aber vergeblich:
#define Mon 0
#define Tue 1
...
#define Sun 6
typedef int day;
int main () {
    day x = Sat;
    day y = x * x;
}
im wesentlichen genauso nutzlos:
typedef enum { 
   Mon, Tue, Wed, Thu, Fri, Sat, Sun 
} day;
int main () {
    day x = Sat;
    day y = x * x;
}
Übung: was ist in C++ besser?
enum Day {
    Mon, Tue, Wed, Thu, Fri, Sat, Sun;
    public static void main (String [] argv) {
        for (Day d : Day.values ()) {
            System.out.println (d);
        }
    }
}
verhält sich wie Klasse
(genauer: Schnittstelle mit 7 Implementierungen)
siehe Übung (jetzt oder bei Objekten)
with Ada.Text_Io; procedure Day is type Day is ( Mon, Tue, Thu, Fri, Sat, Sun ); subtype Weekday is Day range Mon .. Fri; X, Y : Day; begin X := Fri; Ada.Text_Io.Put (Day'Image(X)); Y := Day'Succ(X); Ada.Text_Io.Put (Day'Image(Y)); end Day;
mit Bereichsprüfung bei jeder Zuweisung.
einige Tests können aber vom Compiler statisch ausgeführt werden!
procedure Fruit is
   subtype Natural is 
       Integer range 0 .. Integer'Last;
   type Apples  is new Natural;
   type Oranges is new Natural;
   A : Apples; O : Oranges; I : Integer;
begin -- nicht alles korrekt:
   A := 4; O := A + 1; I := A * A;
end Fruit;
Natural, Äpfel und Orangen sind isomorph,
aber nicht zuweisungskompatibel.
Sonderfall: Zahlenkonstanten gehören zu jedem abgeleiteten Typ.
Typ =
 
 
R = A×B×C
 
Kreuzprodukt mit benannten Komponenten:
 
erstmalig in COBOL (≤1960
 
Übung: Record-Konstruktion (in C, C++)?
 
 
R = A∪B∪C
 
disjunkte (diskriminierte) Vereinigung (Pascal)
 
nicht diskriminiert (C):
 
 
I
 
 
 
 
physikalische Größe =
 
viele teure Softwarefehler durch Ignorieren der Einheiten.
 
in F# (Syme, 200?), aufbauend auf ML (Milner, 197?)
 
 
http://msdn.microsoft.com/en-us/library/dd233243.aspx
 
 
 
Haskell (http://haskell.org/)
 
Java  
 
das ist ein algebraischer Datentyp,
 
die Konstruktoren (Leaf, Nil) bilden die Signatur der Algebra,
 
die Elemente der Algebra sind Terme (Bäume)
 
 
BA : = {f : A→B}
 
ist sinnvolle Notation, denn 
| B|| A| =  
spezielle Realisierungen:
 
 
Design-Entscheidungen:
 
 
 
 
 
 
 
 
dynamische Dimensionierung und Allokation,
Bereichsprüfungen. Nicht notwendig rechteckig.
 
 
 
Unterschiede zwischen
 
 
 
Das geht:
 
(es ist nicht  
 
 
Designfrage: kann ein Feld (auch: String) 
seine Größe ändern?
 
(C: wird sowieso nicht geprüft, Java: nein, Perl: ja)
 
in Java: wenn man das will, dann will man
statt Array eine LinkedList, statt String einen StringBuffer.
 
wenn man mit Strings arbeitet, 
dann ist es meist ein Fehler:
 
benutze Strings zwischen Programmen, 
 
ein einem Programm: benutze immer anwendungsspezifische
Datentypen.
 
...deren externe Syntax spiel überhaupt keine Rolle
 
 
es wird oft als Argument für C (und gegen Java)
angeführt, daß die erzwungene Bereichsüberprüfung
bei jedem Array-Zugriff so teuer sei.
 
sowas sollte man erst glauben, wenn man es selbst
gemessen hat.
 
modernen Java-Compiler sind sehr clever
und können theorem-prove away
(most) subscript range checks
 
das kann man auch in der Assembler-Ausgabe
des JIT-Compilers sehen.
 
 
 
 
implizite Verweise:
 
 
Verweis-Semantik (wie in Java)
 
Wert-Semantik 
  
 
 
 
Rekursion unter Verwendung von Verweistypen
 
Pascal:
 
C: ähnlich, benutze typedef
 
 
 
 
 
typedef struct {
    A foo;
    B bar;
    C baz;
} R;
R x; ...  B x.bar; ...
type tag = ( eins, zwei, drei );
type R = record case t : tag of
    eins : ( a_value : A );
    zwei : ( b_value : B );
    drei : ( c_value : C );
end record;
typedef union {
    A a_value; B b_value; C c_value;
}
interface I { }
class A implements I { int foo; }
class B implements I { String bar; }
Notation dafür in Scala (http://scala-lang.org/)
abstract class I
case class A (foo : Int) extends I
case class B (bar : String) extends I
Verarbeitung durch Pattern matching
def g (x : I): Int = x match {
    case A(f) => f + 1
    case B(b) => b.length()  }
[<Measure>] type kg ;;
let x = 1<kg> ;;
x * x ;;
[<Measure>] type s ;;
let y = 2<s> ;;
x * y ;;
x + y ;;
data Tree a = Leaf a 
            | Branch ( Tree a ) ( Tree a )
data List a = Nil | Cons a ( List a )
interface Tree<A> { }
class Leaf<A> implements Tree<A> { A key }
class Branch<A> implements Tree<A> 
  { Tree<A> left, Tree<A> right }
 BA
BA 
die unterschiedliche Notation dafür (Beispiele?) 
ist bedauerlich.
int main () {
    int a [10][10];
    a[3][2] = 8;
    printf ("%d\n", a[2][12]);	
}
statische Dimensionierung,
dynamische Allokation,
keine Bereichsprüfungen.
Form: rechteckig, Adress-Rechnung:
int [M][N];
a[x][y]  ==>  *(&a + (N*x + y))
int [][] feld = 
         { {1,2,3}, {3,4}, {5}, {} };
for (int [] line : feld) {
    for (int item : line) {
       System.out.print (item + " ");
    }
    System.out.println ();
}
inint [][] a
int [,]  a
int a [] = {1,2,3};
int b [] = {4,5};
int c [] = {6};
    e    = {a,b,c};
printf ("%d\n", e[1][1]);
aber welches ist dann der Typ von e?
int e [][].)
aber niemals innerhalb eines Programms.
explizite Verweise in C, Pascal
Testfall:class ... ist:
struct ... ist:
class s {public int foo; public string bar;}
s x = new s(); x.foo = 3; x.bar = "bar";
s y = x; y.bar = "foo";
Console.WriteLine (x.bar);
und dann class durch struct ersetzen
type Tree = ^ Node ;
type Tag = ( Leaf, Branch );
type Node = record case t : Tag of
  Leaf : ( key : T ) ; 
  Branch : ( left : Tree ; right : Tree );
end record;