Weitere Beispiele

Martin Sulzmann

Die Programmiersprache C (basics)

IO, Funktionen


/*

IO mit printf/scanf

Funktionen und Vorwärtsreferenzen

 */


#include <stdio.h>

void testPrintf() {
  int i = 5;
  float f = 3.2;
  char c = 'a';

  /*
   Beobachtung:
     Benoetigen Konvertierungsfunktionen
        int -> string
        float -> string
        char -> string

   Impliziert
    printInt welche ein int als Argument nimmt
    printFloat welche ein float als Argument nimmt
    ...

   Fuer den User, brauchen was "komfortableres".

   */

  printf("\n i = %d", i);
  printf("\n f = %f", f);
  printf("\n c = %c", c);

  printf("\n i = %d, f = %f, c = %c", i, f, c);


 // Typinformation kodiert im Formatstring
 // Z.B. Platzhalter %f steht fuer eine float Zahl.
 // printf ist eine "variadic" function, nimmt einen Variable Anzahl von Argumenten.

 // Beachte:
 // Typen der Argumente muessen mit Platzhaltern kompatibel sein.


 // Hier sollte anstatt %f entweder %c oder %d stehen.
 // printf ("Characters: %c %f \n", 'a', 65);

 // Zuviele Argumente.
 // printf ("Characters: %c %c \n", 'a', 65, 444);

 // Beachte. Undefiniertes Verhalten.
 // Werden Default Werte verwendet?

 // printf ("Characters: %c %c %d %f %s \n", 'a');

 // printf ("float: %c %c \n", 3.1416, 2.5);
}

// Funktionsdeklaration.
// Teile dem Compiler mit, es gibt eine Funktion von diesem Namen.
// Deren Funktionsdefinition findet sich unten.
// Bekannt als "forward declaration".
// Notwendig im Fall von sich gegenseitig aufrufenden Funktionen.

void testScanf();

int main() {
  // testPrintf();
    testScanf();
}


void testScanf () {
  int i;

  /*

    Bemerkung:
          Endezeichen ist "return".

   Betrachte Eingabe.
   Benoetigen das aequivalent zu printInt, ...
    scanInt    : string -> int
    scanFloat  : string -> float
    scanChar   : string -> char

    Ausgabe:

          printf("\n i = %d", i);

    Eingabe:

          scanf("\n i = %d", i);


         Woher weiss, scanf wo "i" im Speicher liegt.
         Beachte, Variable i wird hier als Wert uebertragen.

         D.h. wir benoetigen

         i = scanfInt("\n i = %d");


         ??? Keine praktikable Loesung, viel zu umstaendlich.

       Idee:
         Was ist falls wir die Referenz auf i an scanf uebergeben koennen?
   */


  printf ("\n Eingabe Dezimalzahl: ");
  scanf ("%d",&i);

  printf("\n i = %d", i);


  // Was bedeutet &i?

  // "&" ist der Adressoperator.
  // 1. Liefert die (Speicher)adresse der Variablen i.
  // 2. "scanf" wandelt den Eingabestring in einen Dezimalwert um.
  // 3. Dieser Dezimalwert wird an der Speicheradresse, an der
  //    Variable i sich befindet abgelegt.

  // Beobachtungen.

  // Mittels "&" kann man also call-by-reference darstellen.

  // Wieso wird der Dezimalwert nicht per Rueckgabe geliefert?
  // Z.B.
  // i = scanf("%d");
  //
  // Dann waere scanf einen Funktion mit Argument string und Rueckgabe int.
  //
  // Was ist mit der Eingabe von Zeichen?
  // char c;
  // c = scanf("%c");
  //
  // In diesem Fall waere scanf eine Funktion mit Argument string und Rueckgabe char.
  //
  // Solche eine Form von "overloading" ist nicht erlaubt!


}

Strukturen, Zeiger, call-by-value versus call-by-reference

/*

Strukturen

Zeiger

Call-by-value versus call-by-reference

 */


#include <stdio.h>


// C hat Strukturen.

// structs in C = classes in Java aber:
//  - keine Methodendeklarationen
//  - alle Feldelemente sind oeffentlich
//  - keine Konstruktoren (und keine Vererbung)

struct Punkt {
  int x;
  int y;
}; // Beachte ";", in Java nicht notwendig

// Sprachgebrauch.
// x ist eine Instanzvariable,
// im C Kontext wird x auch eine Feldvariable genannt.
// Im Englischen "member" ...


void printPunkt(struct Punkt p) {
  printf("\n Punkt = (%d,%d)", p.x, p.y);
}


// User liefert Werte (als Strings eingegeben auf der Konsole)
struct Punkt scanPunkt() {
  struct Punkt p;

  printf("\n Eingabe x:");
  scanf("%d", &p.x);         // Frage: Ist die Interpretation
                             // (a) (&p).x   oder
                             // (b) &(p.x)

  printf("\n Eingabe y:");
  scanf("%d", &p.y);

  return p;
}

// Beachte.

// Elementweise Eingabe.
// Uebergebe an scanf die Referenz auf p.x, "scanf("%d", &p.x);"
//
// Notation:
// p.x Zugriff auf Feldelement x in der Struktur p
// Unaerer Operator &, liefert Referenz.
//
//    &p.x
//
// Wie ist die Interpretation?
//
// (1)  &(p.x)   bedeutet, baue Referenz auf Feldelement x in p
//
// oder
//
// (2)  (&p).x   bedeutet, baue Referenz auf p und wir
//               interpretieren die Referenz als Struktur und
//               greifen wir auf das Feldelement x zu.
//
//  Der Compiler waehlt die Interpretation (1).
// In C gilt, dass "." bindet staerker als "&".


// In C gilt call-by value.
// Deshalb ist die Eingabe nicht von aussen sichtbar.
void scanPunkt2(struct Punkt p) {
  struct Punkt r;
  r = scanPunkt();
  p = r; // Bedeutet, p.x = r.x; p.y = r.y;
}

// Call-by-reference via Zeiger ("pointer").
void scanPunktRef(struct Punkt* p) {
  struct Punkt r;

  r = scanPunkt();

  (*p).x = r.x;  // Beachte: *p.x bedeutet Zugriff auf x und danach Dereferenzierung.
  (*p).y = r.y;
}

// p ist eine Zeigervariable, siehe obige Typdeklaration "struct Punkt* p".

// Betrachte
// (*p).x
//  (1) Via (*p) greifen wir auf die Struktur
//  (2) Via .x greifen wir auf das Element mit dem Namen x zu.
//
//  Was bedeutet dann "*p.x"?
// "." bindet am staerksten, deshalb wird "*p.x" interpretiert als "*(p.x)".
// In unserem Fall liefert
// "*(p.x)" einen Fehler (weil p ist die Referenz und keine Struktur).


void testPunkt() {
  struct Punkt p = {1,2};
  struct Punkt q,r;

  printPunkt(p);

  q = scanPunkt();
  printPunkt(q);
}

// Liefert nicht die erwartete Ausgabe
void testPunkt2() {
  struct Punkt t;
  t.x = 3;
  t.y = 4;

  scanPunkt2(t);
  // An der Stelle kopieren wir
  // 3 nach p.x
  // 4 nach p.y
  // Annahme, p ist der Parameter
  // von scanPunkt2.
  printPunkt(t);
}

void testPunktRef() {
  struct Punkt p;

  scanPunktRef(&p);
  printPunkt(p);
}


int main() {
  // testPunkt();
  // testPunkt2();
  testPunktRef();
}


// Folgende Varianten sind aequivalent zu scanPunktRef

void scanPunktRef2(struct Punkt* p) {
  struct Punkt r;

  r = scanPunkt();

  p->x = r.x;
  p->y = r.y;
}


void scanPunktRef3(struct Punkt* p) {
  struct Punkt r;

  r = scanPunkt();

  *p = r;
}

void scanPunktRef4(struct Punkt* p) {

  *p = scanPunkt();
}


// Diese Variante funktioniert NICHT.
// Variable r ist allokiert auf dem Stack.
// Deren Speicherbereich wird nach Ruecksprung geloescht.
void scanPunktRef5(struct Punkt* p) {
  struct Punkt r;

  r = scanPunkt();

  p = &r;
}

Die Programmiersprache C (“pointer + memory”)

Die Programmiersprache C.

Arrays, Strings, Zeigerarithmetik

#include <stdio.h>

  // Arrays in C.
  // Alle Elemente sind vom gleichen Typ.
  // In unserem Fall. Ein Array von chars.

  // Ein C string ist dargestellt als ein Array von chars.
  // Beachte. In Java gibt es einen string Datentyp welcher auch die
  // Laenge des Arrays verwaltet.
  // In C wird folgender Trick angewandt um die Laenge des strings zu verwalten.
  // Das Ende eines strings in C wird mit einem speziellen ASCII Code gekennzeichnet.
  // Der ASCII Code 0 ("Null") wird verwendet, um das Ende des strings zu markieren.
  // Dies wird auch als "Null Terminierung" bezeichnet und diesen
  // speziellen ASCII Code nennen wir auch Null Terminator.


/* Berechne die Laenge eines strings.
   strings sind Arrays von chars.

    "" hat die Laenge Null.
    "a" hat die Laenge eins.

*/

int len(char s[]) {
  int i = 0;

  // Teste die einzelnen Zeichen, bis wir auf die Null Terminierung treffen.
  while(s[i] != '\0') {
    i++;
  }
  return i;
}


// Es gibt keine Arrays in C.
// Arrays in C werden durch Zeiger emuliert.
// Ein Array ist dargestellt als Zeiger auf das erste Element.

// Variable p ist eine Referenz (aka Zeiger, "pointer") auf ein Zeichen (char).
// In C, ist der Name des Arrays immer die Referenz auf das erste Element in dem Array.
int len2(char* p) {
  int i = 0;

  // Beachte. Dereferenzierung, via "*p" greifen wir auf das Zeichen zu auf welches
  // der Zeiger p referenziert.
  while(*p != '\0') {
    p++;
    // Kurzform fuer
    // p = p + 1;
    // Der Zeiger p verweist auf das naechste Element im Speicher.
    // Beachte. Alle Elemente sind vom gleichen Typ.
    // "+1" bedeutet, der Compiler kennt die Groesse (in Bytes) eines Elements und
    // kann damit die absoluten Speicheradressen berechnen.

    i++;
  }
  return i;
}


void testStrings() {

  char str[10] = "Hello";

  // Die Groesse des Arrays ist hier implizit.
  // Frage: Wieviele Zeichen sind in dem Array str?
  // Die logische Antwort ist 5, aber diese ist falsch!!!!
  // Es sind 5+1 = 6 Zeichen.
  // "+1" wegen dem Null Terminator.

  char str2[] = "123456"; // 6 + 1 Zeichen werden allokiert.
  // Beachte. Der Compiler berechnet automatisch die Groesse des Arrays.
  // Im Fall von strings wird immer "eins dazugerechnet", wegen dem Null Terminator.
  int i;

  char str3[] = { '1', '2' }; // 1. Der Compiler allokiert nur 2 Zeichen.
                              // 2. Die Null Terminierung fehlt.
  char str4[] = { '1', '2', '\0' };
  // Beachte:
  // Im Fall wir initialisieren ein Array von chars Elementweise,
  // wird NICHT automatisch der Null Terminator hinzugefuegt.


  printf("\n");

  for(i = 0; i < 5; i++) {
    printf("%c",str[i]);
    // Zugriff auf ein Array wie in Java.
  }

  printf("\n len(str) = %d", len2(str));

  printf("\n len(str2) = %d", len(str2));

  printf("\n len(str3) = %d", len(str3));

  printf("\n len(str4) = %d", len2(str4));

}

/*

Beispiel Ausgabe (auf meinem mac)

Hello
 len(str) = 5
 len(str2) = 6
 len(str3) = 3     *******
 len(str4) = 2%

D.h.

 str3 angelegt als char Array der Groesse, bestehend aus '1' und '2',
 dies sind die Positionen str3[0] und str3[1].

 Wie's scheint liegt an Position str3[2] irgendein Zeichen
 und an Position str3[3] liegt der Null Terminator.

 => Es gibt keinen Array-out-bounds Check in C !!!!!!!!!!!!!


 */


void testArrayVsPointer() {
  int x[5] = {1,2,3,4,5};
  int i;
  int* p;

  printf("\n");
  for(i=0; i < 4; i++) {
    printf("%d ",x[i]);   // x[i]  Zugriff auf Element in x an der Position i
  }


  printf("\n");
  for(i=0; i < 4; i++) {
    printf("%d ",*(x+i)); // *(x+i)  von x ausgehend, gehe i Element im Speicher nach oben
  }

  printf("\n");
  p = x;
  // p = &x[0];
  for(i=0; i < 4; i++, p++) {
    printf("%d ",*p);
  }

}



int main() {
  printf("\n");

  testArrayVsPointer();
  testStrings();

}

Extra Beispiel

#include <stdio.h>

int len(char s[]) {
  int i = 0;

  // Teste die einzelnen Zeichen, bis wir auf die Null Terminierung treffen.
  while(s[i] != '\0') {
    i++;
  }
  return i;
}

void test2() {

  char s2[6] = {'H','a', 'l', 'l', 'o'};     // WARNUNG. '\0' fehlt!
  char s[6] = {'H','a', (char)(32), 'l', 'o', '\0'};

  char s3[] = "Hallo";
  // Groesse des Arrays wird automatisch berechnet. Hier 6 Elemente.
  // Compiler fuegt automatisch die '\0' ein.

  int i;
  char* p = &s3[0] ; // Referenz auf das erste Zeichen im Array s3

  for(i=0; i < len(s3); /*i++, p++ */) {
    printf("%c", *p);
    i = i + 1;
    p = p + 1; // Nachfolger von p !!
    //p = p + 2; // Nachfolger vom Nachfolger von p !!
  }

  printf("\n");
  p = &s3[0]; // Referenz auf das erste Zeichen in s3.
  for(i=0; i < len(s3); i++) {
    printf("%c", *(p + i)); // p + i  verweist auf den iten Nachfolger
                            // *(p + i) liest den entsprechenden Wert
  }

  // In C ist der Name des Arrays die Referenz auf das erste Zeichen!
  printf("\n");
  for(i=0; i < len(s3); i++) {
    printf("%c", *(s3 + i)); // s3[i] ist syntaktischer Zucker fuer *(s3 + i)
  }

  /*

  for(i=0; i < len(s3) + 50; i++) {
    printf("%c", s3[i]);
    // Kein "array out of bounds check" in C!
  }
  */

  printf("\n %d", len(s));
  printf("\n %d", len(s2));
  printf("\n %d", len(s3));

}


int main() {

  test2();

}

Funktionszeiger

// Funktionszeiger.

#include <stdio.h>

// Parameter f ist ein Zeiger (Referenz) auf eine Funktion.
// Diese Funktion erwartet einen int Wert und liefert einen int Wert zurueck.
void mapInt(int* p, int len, int (*f)(int)) {
 int i;
 for(i=0; i<len; i++)
   p[i] = (*f)(p[i]);
}


int inc(int x) { return x+1; }
int square(int x) { return x*x; }


int main() {
   int x[5] = { 1,2,3,4,5 };
   mapInt(x,5,&inc);
   mapInt(x,5,&square);

   printf("%d", x[1]);

   return 0;
}


// Beachte.
// Oft werden "shorthands" via typedef verwendet.

typedef int (*intFunc)(int);

void mapInt2(int* p, int len, intFunc f) {
 int i;
 for(i=0; i<len; i++)
   p[i] = (*f)(p[i]);
}

Manuelle Speicherverwaltung (und Probleme die dabei auftauchen)

// Komplexe Datentypen (Strukturen)
// zur Beschreibung von Arrays mit Laengeangabe.

// Manuelle Speicherverwaltung.

#include <stdio.h>
// printf, ...
#include <stdlib.h>
// malloc, ...

// Integer array.
// Zeiger start verweist auf erstes Element.
// len beschreibt die Laenge.
struct IntArray {
  int* start;
  int len;
};

void print_IntArray(struct IntArray x) {
  int i=0;
  int* p = x.start;

  for(i=0; i<x.len; i++, p++) {
    printf("%d", *p);
  }


  /*
  // oder via array Zugriff
  for(i=0; i<x.len; i++) {
    printf("%d", x.start[i]);
  }
  */

}

// Wir wollen auf dem heap ein integer Array
// der Groesse n mit den Intialwerten intial anlegen.
struct IntArray new_IntArray(int n, int initial) {
  int* p = malloc(n * sizeof(int)); // Allokiert Bytes im Speicher
                                    // Speicher fuer n Integer Werte
  // alloc ist eine Art von "new", man muss aber den Speicher in Bytes allokieren.
  struct IntArray x = {p, n};
  int i = 0;

  for(i=0; i<n; i++, p++) {
    *p = initial;
    // Beachte.
    // Typinformation, hier int*, ist wichtig,
    // um die Dereferenzierung auszufuehren.
  }

  return x;
}

// In C, manuelle Speicherkontrolle.
// Deshalb muss der via malloc allokierte Speicher, explizit freigegeben werden.
void delete_IntArray(struct IntArray x) {
  free(x.start);
}

int len_IntArray(struct IntArray x) {
  return x.len;
}

void testIntArray() {
  struct IntArray x = new_IntArray(8,14);
  struct IntArray y = new_IntArray(4,3);

  print_IntArray(x);
  print_IntArray(y);


  delete_IntArray(x);
  delete_IntArray(y);


  print_IntArray(y);

  // Beachte. Potentielle Speicherverletzung.
  // Speicher als "free" deklariert. Aber wir greifen auf "x" zu.
  // Keine Garantie
  //  (a) welche Werte sich in dem IntArray befinden,
  //  (b) ob Zugriff moeglich.

}

void testIntArray2() {
  struct IntArray x = new_IntArray(8,14);
  struct IntArray y = new_IntArray(4,0);

  print_IntArray(x);
  print_IntArray(y);

  delete_IntArray(x);

  // Speicherleck.
  // Es fehlt
  // delete_IntArray(y);

}


void testIntArray2b() {
  struct IntArray x = new_IntArray(8,14);
  struct IntArray y = new_IntArray(4,0);

  print_IntArray(x);
  print_IntArray(y);

  delete_IntArray(x);

  delete_IntArray(y);
  delete_IntArray(y); // doppeltes delete!

}

void testIntArray3() {
  struct IntArray x = new_IntArray(8,14);
  struct IntArray y;

  y = x;
  // Elementweises kopieren:
  // x.start = y.start;
  // x.len = y.len;
  // Die "start" Zeiger in x und y
  // verweisen auf den gleichen Speicherbereich.


  print_IntArray(x);
  print_IntArray(y);

  delete_IntArray(x);
  // impliziert den Aufruf free(x.start)

  delete_IntArray(y);
  // impliziert den Aufruf free(y.start)

  // Es gilt.
  // Speicher kann nur einmal freigegeben werden!
  // Zweifacher "free" Aufruf mit gleichem Zeiger
  // fuehrt zum Absturz.

  // Problemstellung hier: Pointer aliasing
  // Wie koennen wir herausfinden welcher Speicher
  // wurde schon freigeben?
  //
  // In C gibt es wenig "Komfort",
  // es wird viel, viel besser in C++ (oder wir verwenden eine Sprache mit automatischer Speicherverwaltung).
}

void testIntArray4() {
  struct IntArray x = new_IntArray(8,14);
  struct IntArray y;

  x = y;

  print_IntArray(x);
  print_IntArray(y);

  delete_IntArray(y);

  // y nicht initialisiert, ueberschreibt x!
  // Probleme sind:
  // 1. Speicherleck, da delete_IntArray(x) fehlt
  // 2. Zugriff auf x und y (da ueberschrieben und nicht initialisiert).
  // 3. Willkuehrliche Freigabe von Speicher in delete_IntArray(y), da nicht initialisiert.

}


int main() {
  printf("\n");


  // testIntArray();

  // testIntArray2();

  // testIntArray2b();

  // testIntArray3();
  // Absturz wegen zweifachem "free" des gleichen allokierten Speichers.

  testIntArray4();
}

Die Programmiersprache C (Testverfahren, “generics”, OO)

Die Programmiersprache C.

Testverfahren (User, Unit, und Invarianten Tests)

#include <stdio.h>
#include <stdlib.h>


// Punkt im 2-dimensionalen Koordinatensystem
typedef struct { int x; int y; } point;



// Gleichheit zwischen Punkten
int eqPoint(point p, point q) {
  return (p.x == q.x) && (p.y == q.y);
}

// Ausgabe eines Punktes
void printPoint(point p) {
  printf("\n (x = %d, y = %d)", p.x, p.y);
}

// Koordinaten vertauschen
// Buggy !!!!!!!!!!!!
point switchPoint(point p) {
  p.x = p.y;
  p.y = p.x;

  return p;
}

//////////////////////
// Tests

// User Tests

void testUser() {
  point p = {2,2};

  printPoint(p);
  printPoint(switchPoint(p));
}

// Unit Tests

typedef struct { point input; point expected; } TestCase;

void testUnit() {
  int i;

  TestCase tests[] = {
    {{1,1}, {1,1}},
    {{2,2}, {2,2}}
  };


  for(i=0; i<2; i++) {
    if(eqPoint(switchPoint(tests[i].input),
           tests[i].expected)) {
      printf("\nOK");
    } else {
      printf("\nFAIL");
    }
  }
}

// Invarianten = Properties = Eigenschaften


int property1(point p) {
  return eqPoint(switchPoint(switchPoint(p)),
         p);
}

void testInvariante() {
  int i;
  point ps[10];

  // Generiere 10 Punkte zufaellig.
  for(i=0; i<10; i++) {
    ps[i].x = rand();
    ps[i].y = rand();
  }

  for(i=0; i<10; i++) {
    if(property1(ps[i])) {
      printf("\nOK");
    } else {
      printf("\nFAIL");
    }
  }

}

int main() {
  testUser();
  testUnit();
  testInvariante();
}

Generische Datentypen und Funktionen in C

// Generische Datentypen und Funktionen in C.

#include <stdio.h>
#include <stdlib.h>

enum Type {
  INT = 0,
  FLOAT = 1
};

// Generische Variante
// void* ist der generischer Zeiger in C.
// Siehe "Object" in Java.
// void* ist wie ein Zeiger ohne Typinformation.
struct GenArray {
  enum Type type;
  void* start;
  int len;
};

int sizeOf(enum Type type) {
  switch(type) {
  case INT: return sizeof(int);
  case FLOAT: return sizeof(float);
  }
}

struct GenArray new_GenArray(int n, enum Type type) {
  void* q = malloc(n * sizeOf(type));

  struct GenArray x = {type, q, n};

  return x;
}


// Dereferenzierung von void* nicht moeglich.
// Wir verwenden die in dem enum kodierte Typinformation,
// um auf int*, float*, etc. zu casten.
void print_Type(void* elem, enum Type type) {
  int* i;
  float* f;

  switch(type) {
  case INT:
    i = (int*)(elem);
    printf("%d", *i);
    break;
  case FLOAT:
    f = (float*)(elem);
    printf("%f", *f);
    break;
  }
}

// Pointer Arithmetik fuer generischen Zeiger.
// Die in dem enum kodierte Typinformation ist wiederum wichtig.
void* inc_pointer_Type(void* p, enum Type type) {
  int* i;
  float* f;

  switch(type) {
  case INT:
    i = (int*)(p);
    i++;
    p = i;
    break;
  case FLOAT:
    f = (float*)(p);
    f++;
    p = f;
    break;
  }
  return p;
}

void print_GenArray(struct GenArray x) {
  int i=0;
  void* p = x.start;

  for(i=0; i<x.len; i++) {
    print_Type(p, x.type);
    p = inc_pointer_Type(p, x.type);
  }
}

void update_GenArray(struct GenArray x, int pos, void* new_Val) {
  int* i; int* i_Val;
  float* f; float* f_Val;

  // "array" out-of-bounds check
  if(pos >=0 && pos < x.len) {
    switch(x.type) {
    case INT:
      i = (int*)(x.start);
      i_Val = (int*)(new_Val);
      i[pos] = *i_Val;
      break;
    case FLOAT:
      f = (float*)(x.start);
      f_Val = (float*)(new_Val);
      f[pos] = *f_Val;
      break;
    }
  }

}

void test_GenArray() {
  struct GenArray x = new_GenArray(2, INT);
  struct GenArray y = new_GenArray(3, FLOAT);
  int i = 5;
  float f = 1.0;

  update_GenArray(x,0,&i);
  update_GenArray(x,1,&i);

  update_GenArray(y,0,&f);
  update_GenArray(y,1,&f);
  update_GenArray(y,2,&f);

  print_GenArray(x);

  print_GenArray(y);
}

int main() {
  printf("\n");
  test_GenArray();
}

OO in C selbst nachgebaut

#include <stdio.h>
#include <stdlib.h>

/*

Geometric objects.

Java pseudo-code.

class Shape {
 int area();
}

class Rectangle extends Shape {
  int length;
  int width;
  int area() { return length * width; }
}

class Square extends Shape {
  int length;
  int area() { return length * length; }
}

// Annahme, frei-stehende Funktionen in Java moeglich.
int sumArea (Shape s1, Shape s2) {
  return s1.area() + s2.area();
}

 */

// Geometric objects in C

typedef struct {
  int length;
  int width;
} rectangle;

typedef struct {
  int length;
} square;


int areaRectangle(rectangle* r) {
  return r->length * r->width;
}

int areaSquare(square* s) {
  return s->length * s->length;
}


// Generic representation

typedef struct {
  void* obj;
  int (*area)(void*); // Function pointer
} shape;


// Generic function
int sumArea(shape s1, shape s2) {
  return s1.area(s1.obj) + s2.area(s2.obj);
}

// Generic functionc (same as above)
int sumArea2(shape s1, shape s2) {
  return (*(s1.area))(s1.obj) + s2.area(s2.obj);
}

// Points to note.
// In modern C, there's no need to dereference a function pointer.
// "." binds tighter than "*".
// Hence, the expression
//     (*(s1.area))(s1.obj)
// is equivalent to
//     s1.area(s1.obj)

// Some examples
void testShape() {
  rectangle r = {1,2};
  square s = {3};
  shape s1 = {&r, (int (*)(void *))(&areaRectangle)};
  shape s2 = {&s, (int (*)(void *))(&areaSquare)};
  shape s3 = {&s, (int (*)(void *))(&areaRectangle)};   // (P)
  shape s4 = {&r, (int (*)(void *))(&areaSquare)};      // (Q)

  printf("\n %d", sumArea(s1,s2));

  printf("\n %d", sumArea(s3,s4));
}

/*

Points to note.

We emulate here "OO" in C. The user needs to precisely follow some coding guidelines.

Consider (P) and (Q).
The "object" (structure) does not match the "method" (function).
The coding guidelines for emulating OO in C are broken.
This may lead to some run-time errors.

*/


int main() {
  testShape();
}

Die Programmiersprache C++ (basics)

Sichtbarkeitsbereiche, Strings und IO, Referenzübergabe

// Sichtbarkeitsbereiche.
// Strings und IO.
// Referenzübergabe

#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;


// Sichtbarkeitsbereiche ("scope")
// In C gibt es nur 2 Sichtbarkeitsbereiche.
// (Im neusten C Standard gilt die gleiche Regel wie in C++)
// In C++ sind beliebige Schachtelungen moeglich.
// Jeder Codeblock { ... } entspricht einem Sichtbarkeitsbereich.

void scopeInC() {
  float i = 1.0;
  int x[5] = {1,2,3,4,5};
  int i_2;
  int j;
  int x_3;

  for(i_2 = 0; i_2 < 5; i_2++) {
    j = x[i_2];
    x_3 = j;
    printf("%d",x_3);
  }

}

// B = Begin. E = End.
// Markiert Codeblock.
void scopeInCPP() { // B1
  float i = 1.0;
  int x[5] = {1,2,3,4,5};

  for(int i=0; i < 5; i++) { // B2
    int j = x[i];
    { // B3
      int x = j;
      printf("%d", x);
    } // E3
  } // E2

} // E1

// Strings.
// IO in C++ ist statisch Typsicher Dank überladener Operatoren/Funktionen


void IO_in_CPP() {
  string s = "\nHallo";

  // Sende String zur Konsole.
  cout << s;

  float f = 1.0;
  // Entspricht printf("%f",f)
  cout << f;

  int i = 5;
  // Entspricht printf("%d",i)
  cout << i;

  // "<<" ist ein binaerer Operator. Links ist das Ausgabeziel und rechts der Ausgabewert.
  // "<<" ist ueberladen, auf der rechten Seiten koennen int, float, .... Werte stehen.


  // Sequenz von Ausgaben.
  cout << f << "  " << i;


  // "<<" ist links assoziativ.
  ((cout << f) << "  ") << i;
  // Beobachtung:
   // cout << f, rechter Operand ist vom Typ float.
  // (...) << "  ",  rechter Operand ist vom Typ string.
  // (...) << i, rechter Operand ist vom Typ int.

  cout << i << "Hallo" << f;

  ((cout << i) << f) << i << f << "Hallo";
}



// Referenzübergabe.
// Syntaktischer Zucker in C++.


struct Pair {
  int left;
  int right;
};

// Variante 1.
void swapPair_Ref(struct Pair& p) {
  int tmp = p.left;
  p.left = p.right;
  p.right = tmp;
}

// Variante 2.
void swapPair_Pointer(struct Pair* p) {
  int tmp = p->left;
  p->left = p->right;
  p->right = tmp;
}


void testSwap() {
  struct Pair p = {1,2};
  struct Pair q = {1,2};

  cout << "\nPair: (" << p.left << "," << p.right << ")";
  swapPair_Ref(p);
  cout << "\nPair swapped: (" << p.left << "," << p.right << ")";

  cout << "\nPair: (" << q.left << "," << q.right << ")";
  swapPair_Pointer(&q);
  cout << "\nPair swapped: (" << q.left << "," << q.right << ")";

}



int main() {

  // scopeInC();
  // scopeInCPP();
  // IO_in_CPP();
  // testSwap();

}

Überladene Operatoren/Funktionen

// C++ unterstuetzt überladene Operatoren/Funktionen.
// Regel:
// Instanz muss eindeutig bestimmbar sein via Argumenttypen.

#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;



// Eigene Instanz fuer "<<" Operator.
// Man spricht hier von "Überladung".
struct Pair {
  int left;
  int right;
};


ostream& operator<< (ostream &out, struct Pair &p) {
  out << "(" << p.left << "," << p.right << ")";

  return out;
}


// Transformation des Compilers.
// Generiere eindeutigen Namen, normalerweise Hash-Index
// basierend auf Argumenttypen.
// Wir verwenden einfach "_Pair".

ostream& outStream_Pair (ostream &out, struct Pair &p) {
  out << "(" << p.left << "," << p.right << ")";

  return out;
}


void IO_in_CPP() {
  string s = "\nHallo";

  struct Pair p = {1,2};
  cout << p;   // Verwende obige "out stream" Instanz.
  // Compiler Transformation.
  // Verwende eindeutigen Funktionsaufruf.
  outStream_Pair(cout,p);

}



// Beachte:
//  - swapPair ist eine überladene Funktion.
//  - Mehrfache Definition, aber für verschiedene Argumenttypen.

// Variante 1.
void swapPair(struct Pair& p) {
  int tmp = p.left;
  p.left = p.right;
  p.right = tmp;
}

// Intern passiert folgendes.

// Annahme, der Funktionsname swapPair_Ref ist eindeutig.
void swapPair_Ref(struct Pair& p) {
  int tmp = p.left;
  p.left = p.right;
  p.right = tmp;
}

// Weitere Transformation.
// Uebersetze call-by-reference in call-by-value + pointer

void swapPair_Ref_Transformed(struct Pair* p) {
  int tmp = p->left;
  p->left = p->right;
  p->right = tmp;
}

// Variante 2.
void swapPair(struct Pair* p) {
  int tmp = p->left;
  p->left = p->right;
  p->right = tmp;
}

void swapPair_Pointer(struct Pair* p) {
  int tmp = p->left;
  p->left = p->right;
  p->right = tmp;
}

// Variante 3 (call-by-value)
/*

"'swapPair' is ambiguous"


void swapPair(struct Pair p) {
  int tmp = p.left;
  p.left = p.right;
  p.right = tmp;
}

*/


void testSwap() {
  struct Pair p = {1,2};
  struct Pair q = {1,2};

  cout << "\nPair: " << p;
  swapPair(p);
  // Compiler verwendet hier Variante 1 mit explizitem eindeutigem Namen.
  // 1ter Transformationsschritt.
  swapPair_Ref(p);
  // 2ter Transformationsschritt.
  swapPair_Ref_Transformed(&p);
  cout << "\nPair swapped: " << p;

  cout << "\nPair: " << q;
  swapPair(&q);
  swapPair_Pointer(&q);
  cout << "\nPair swapped: " << q;

}


int main() {

    testSwap();

}

Klassen

#include <iostream>
#include <string>
using namespace std;

// Klassen.
// Erweiterung der Klassenhierarchie ("extends").


///////////////////////
// Klassen

class Point2D {
  float x, y; // private
public:
  Point2D(float x2=0, float y2=0) {
  x = x2;  y = y2;
  }
  void scale(float f) {
  x = f * x; y = f * y;
 }
  float getX() { return x; }
  float getY() { return y; }
  void setXY(float x2, float y2) {
    x = x2; y = y2;
  }

  string show() {

    // + Konkatenation zweier Strings.
    // Konvertierung von float nach String via to_string.
    // C++ unterstuetzt Ueberladung (overloading).
    // D.h wir koennen z.B. to_string auf Werte vom Typ float,
    // als auch auf Werte vom Typ int anwenden.
    // Konkret.
    // string str = to_string(1) + to_string(1.0);
    return ("\nx = " + to_string(x) + "\ny = " + to_string(y));

  }

}; // Point2D


void testPoint2D() {
  Point2D p1; // Wir haben den Default Auruf
              // p1 = Point2D();
              // Aber wir haben Default Werte definiert.
              // Deshalb wir obige Anweisung interpretiert als
              // p1 = Point2D(0,0);
  Point2D p2 = Point2D(1,2);
  Point2D p3(3,4); // Verschieden Schreibweise fuer einen Konstruktoraufruf.
  Point2D p4 = Point2D(1); // Point2D(1,0)

  // Beachte. In Java werden Objekte immer mit Hilfe von "new" angelegt!
  // In Java liegen alle Objekte auf dem Heap.
  // In C++ koennen Objekte auf dem Stack ("kein new") oder dem Heap ("new") liegen.
  // Im obigen Beispiel, liegen die Objekte p1, p2 und p3 auf dem Stack.

  cout << p1.show();
  cout << p2.show();
  p3.scale(2);
  cout << p3.show();
}



// Erweiterung der Klassenhierarchie ("extends").
class Point3D : public Point2D {
  float z;
public:
  Point3D(float x2=0, float y2=0, float z2=0) : Point2D(x2,y2) {
    z = z2;
  }

  void scale(float f) {
    this->setXY(f * this->getX(), f * this->getY());
    z = f * z;
 }

  string show() {
    return ("\nx = " + to_string(getX())
      + "\ny = " + to_string(getY())
      + "\nz = " + to_string(z));

  }

};

void testPoint3D() {
  Point3D p = Point3D(1,2,3);

  cout << p.show();

  p.scale(2);

  cout << p.show();

  // Point3D <= Point2D,
  // bedeutet jedes Objekt der Klasse Point3D kann auch an einer Stelle verwendet werden,
  // an welcher ein Objekt der Klasse Point2D erwartet wird.
  Point2D q = p;

  cout << q.show();

}


int main() {
  testPoint2D();
  testPoint3D();
}

Virtuelle (dynamisch) versus nicht-virtuelle (statisch) Methoden(auswahl) und static

#include <iostream>
#include <string>
using namespace std;

// 1. Virtuelle versus nicht-virtuelle Methoden.
// Virtuelle Methoden
//    => dynamische Methodenauswahl basierend
//       auf dem dynamischen Typ des Objekts
//       zur Laufzeit.
//       In C++ muss zur virtuellen Methodenauswahl,
//       der Methodenaufruf via "pointer objects" stattfinden.
// Nicht-virtuelle Methoden
//    => statische Methodenauswahl basierend
//       auf dem statischen Typ wie deklariert
//       im Programmtext.
// 2. "static"
//   Variablen, Methoden welchen von allen
//   Objekten geteilt werden.

class Vehicle {
public:
  static int number;
  static int getNo() { return number; }
  virtual int maxSpeed() { return 0; }
  string info() { return "I'm a vehicle"; }
};

int Vehicle::number = 0;

// car <= Vehicle
class car : public Vehicle {
public:
  car() { Vehicle::number++; }
  int maxSpeed() { return 100; }
  string info() { return "I'm a car"; }
};


// Bike <= Vehicle
class Bike : public Vehicle {
public:
  Bike() { number++; }
  int maxSpeed() { return 30; }
  string info() { return "I'm a bicycle"; }
};


void test1(Vehicle v) {
  cout << "\nInfo: " << v.info();
  cout << "\nSpeed: " << v.maxSpeed();
}

void test2(Vehicle* v) {
  cout << "\nInfo: " << v->info();
  cout << "\nSpeed: " << v->maxSpeed();
}

void test3(car v) {
  cout << "\nInfo: " << v.info();
  cout << "\nSpeed: " << v.maxSpeed();
}


int main() {
  Vehicle::number = 0;
  Bike b = Bike();
  car c = car();

     test1(c); // car <= Vehicle
               // das car Objekt c wird konvertiert
               // in ein Vehicle Objekt

     test2(&c); // *car <= *Vehicle
                // Die Referenz auf ein car Objekt
                // wird uebergeben.

     test3(c);

     test1(b);
     test2(&b);

  cout << "\n There are "
       // << Vehicle::number
       << Vehicle::getNo()
       << " vehicles";
}

Die Programmiersprache C++ (advanced)

  1. Mehr zu Klassen und Methoden:

    • Method chaining

    • Abstrakte Klassen (= Java Interfaces)

    • Templates

  2. Manuelle Speicherverwaltung in C++ ist trickreich

    • Zu wenige “deletes” (Speicherleck)

    • Zu viele “deletes (es kracht)

    Abhilfen:

    • copy/move Semantik (von Hand selbstgebasteltet)

    • “smart pointers” (clevere Bibliothek welche die copy/move Semantik umsetzt)

Mehr zu Klassen und Methoden

Method chaining

#include <iostream>
#include <string>
using namespace std;


// Method chaining
class Integer {
  int x;
public:
  Integer(int _x=0) { x = _x; }
  Integer div(int y) {
    return Integer(this->x / y);
  }
  string show() {
    return to_string(this->x);
  }
};


void testInteger() {
  Integer i(20);

  cout << i.div(2).div(4).show();

  // Method calls sind rechts-assoziativ.
  cout << ((i.div(2)).div(4)).show();


  // Was ist im Fall von "division by zero"?
  // Propagiere Fehler via "monadic interface".
  // Mehr Details gegen Ende der Vorlesung.
}

int main() {
  testInteger();
}

Abstrakte Klassen (= Java Interfaces)

#include <iostream>
#include <string>
using namespace std;


//////////////////////////////////////////////////
// Abstrakte Klasse = Interface a la Java.

class Shape {
public:
  virtual int area()=0;

};

// Klassendeklaration impliziert die Subtyp Relation  Rectangle <= Shape.
// "<=" ist eine partielle Ordnung abgleitet aus den Klassendeklarationen.
class Rectangle : public Shape {
  int length;
  int width;
public:
  Rectangle(int l=2, int w=3) {
    length = l;
    width = w;
  }

  // Definition fuer Rectangle Objekte.
  int area() { return length * width; }
};


class Square : public Shape {
  int length;
public:
  Square(int l=3) {
    length = l;
  }

  int area() { return length * length; }
};


// Virtuelle Methoden muessen via "pointer objects" aufgerufen werden.
int sumArea(Shape* s1, Shape* s2) {
  return s1->area() + s2->area();

}

void testShape() {
  Rectangle r = Rectangle();
  Square s = Square();

  // Beobachtung:
  // Rectangle <= Shape
  // Also gilt auch
  // Rectangle* <= Shape*
  cout << sumArea(&r,&s);
}

int main() {
  testShape();
}

Templates

#include <iostream>
#include <string>
using namespace std;


///////////////////////////////
// Templates.
// Expansion zur Compile-Zeit.


// Beispiel: Templatifizierte Funktion
//
// Uebergabe via Referenz.
// Vertausche jeweils die beiden Argumente.

template<typename A>
void mySwap(A& x, A& y) {
  A tmp;

  tmp = x;
  x = y;
  y = tmp;
}


void testSwap() {
  int x = 1;
  int y = 2;
  cout << "\n x = " << x << " y = " << y;

  mySwap<int>(x,y);         // Aufruf fuer die Typinstanz "int"

  cout << "\n x = " << x << " y = " << y;

}


// Beispiel: Templatifizierte Funktion mit verschiedenen Instanzen.

template<typename A>
string Show(A);

template<>
string Show(float x) {
  return to_string(x);
}

template<>
string Show(int x) {
  return to_string(x);
}

template<>
string Show(bool x) {
  return x ? "true" : "false";
}

// Beispiel: Templatifizierte Klasse.

template<typename A, typename B>
class Point {
  A x;
  B y;
public:
  Point(A x2, B y2) {
    x = x2; y = y2;
  }

  string show() {
    return ("\nx = " + Show<A>(x) + "\ny = " + Show<B>(y));
  }

};


void testTemplate() {
  cout << "\n" << Show<int>(1);
  cout << "\n" << Show<float>(1.0);
  cout << "\n" << Show<bool>(true);


  Point<int,float> p = Point<int, float>(1, 3.0);

  cout << "\n" << p.show();


}


int main() {
  testSwap();
  testTemplate();
}

Manuelle Speicherverwaltung in C++ ist trickreich

Verwende “–std=c++11”


#include <iostream>
#include <string>
#include <memory>
using namespace std;



// delete zu wenig
/////////////////////////////////////////////////////////////

void ex1() {

  string* p = new string("Hallo");

  cout << *p;

  // Oops!
  // delete p;
}


// delete zu viel
/////////////////////////////////////////////////////////////

void ex2() {

  string* p = new string("Hallo");

  cout << *p;

  // Oops!
  delete p;
  delete p;
}

// Zugriff nach delete
/////////////////////////////////////////////////////////////

void ex2b() {

  string* p = new string("Hallo");

  delete p;

  // Oops!
  cout << *p;

}


// Smart Resource Acquisition Is Initialization (RAII)
/////////////////////////////////////////////////////////////

template<typename T>
class Ptr {
  T* ptr;
public:
  Ptr(T x) {
    ptr = new T(x);
  }
  ~Ptr() {
    delete ptr;
    cout << "deleted";
  }
  T operator*() {
    return *ptr;
  }
};

void ex3() {
  Ptr<string> p("Hallo"); // "new"

  cout << *p;
  // Cool!
  // Derefernzierungsoperator kann "overloaded" werden.
  // Der Programmcode
  //    cout << *p;
  // wird vom Compiler uebersetzt nach
  //    cout << *p.ptr;
}
/*
Bei Ruecksprung.
1. Gebe Speicher belegt von p frei.
2. => Konsequenz ist, der Destruktoraufruf auf p.
3. => delete p.ptr

 */



// Kopieren mit Zeigern.
/////////////////////////////////////////////////////////////

void f(Ptr<string> q) {
  cout << *q;
}


void ex4() {
  Ptr<string> p("Hallo");
  f(p); // Default Kopierkonstruktor!
  // q.ptr = p.ptr
  // Zweifaches delete der gleichen Speicherstelle!
}



// RAII + copy Semantik
/////////////////////////////////////////////////////////////

template<typename T>
class Ptr2 {
  T* ptr;
public:
  Ptr2(T x) {
    ptr = new T(x);
  }
  Ptr2(const Ptr2& src) {
    // Standard Definition: ptr = src.ptr;

    ptr = new T(*src.ptr);
    // Wegen "const" koennen wir nicht (einfacher) schreiben:
    // ptr = new T(*src);
  }
  Ptr2& operator=(const Ptr2& src) {
    if(this != &src) {
      delete ptr;
      ptr = new T(*src.ptr);
    }
    return *this;
  }
  ~Ptr2() {
    delete ptr;
    cout << "deleted";
  }
  T operator*() {
    return *ptr;
  }
};


void h(Ptr2<string> q) {
  cout << *q;
}


void ex5() {
  Ptr2<string> p("Hallo");
  Ptr2<string> q("abc");
  h(p);
  p = q;
  h(p);
  // Wir kopieren dauernd!
}

// RAII + copy/move Semantik
/////////////////////////////////////////////////////////////

template<typename T>
class Ptr3 {
  T* ptr;
public:
  Ptr3(T x) {
    ptr = new T(x);
  }
  Ptr3(const Ptr3& src) {
    ptr = new T(*src.ptr);
  }
  Ptr3(Ptr3&& src) {
    ptr = src.ptr;
    src.ptr = nullptr;
  }
  Ptr3& operator=(const Ptr3& src) {
    if(this != &src) {
      delete ptr;
      ptr = new T(*src.ptr);
    }
    return *this;
  }
  Ptr3& operator=(Ptr3&& src) {
    if(this != &src) {
      delete ptr;
      ptr = src.ptr;
      src.ptr = nullptr;
    }
    return *this;
  }
  ~Ptr3() {
    delete ptr;
    cout << "deleted";
  }
  T operator*() {
    return *ptr;
  }
};

void g(Ptr3<string> q) {
  cout << *q;
}


void ex6() {
  Ptr3<string> p("Hallo");
  Ptr3<string> q("abc");
  g(p);                      // copy
  g(Ptr3<string>("123"));    // move
  p = move(q);               // expliziter move
  g(p);
  // g(q);                   // Auf q sollte nicht mehr zugegriffen werden
}


// smart pointers (clevere Bibliothek welche die copy/move Semantik umsetzt)
/////////////////////////////////////////////////////////////

void i(shared_ptr<string> q) {
  cout << *q;
}

// Recast von ex6 mit Hilfe von smart pointers.
void ex7() {
  shared_ptr<string> p = make_shared<string>("Hallo");
  i(p);
  shared_ptr<string> q = p;
  i(q);
  i(make_shared<string>("123"));
  p = move(q);
  i(p);
  // i(q);
}

int main() {

  // ex1();
  // ex2(); // seg fault
  // ex2b();
  // ex3();
  // ex4(); // seg fault
  // ex5();
  // ex6();
  ex7();
}

Polymorphie in C++ und ein Vergleich zu Java

Thema Polymorphie

Einfache Beispiele

Dann

Coercive und Nominales Subtyping, Overloading

#include <cstdlib>
#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;


/*

- Coercive subtyping
- Nominales subtyping
- Overloading

 */


//////////////////////////////
// Coercive subtyping

int func(float x) {
    if (x < 1.0) return 1;
    return 0;
}

void testCoerce() {
 int arg = 1;
 float res;

 // Typkorrekt weil int <= float,
 // d.h. jeder Wert vom Typ int kann auch an der
 // Programmstelle verwendet werden, an welcher float erwartet wird.
 res = func(arg);
}

float coerce(int x) {
  return (float)x;
}

void testCoerceExplicit() {
 int arg = 1;
 float res;

 res = coerce(func(coerce(arg)));
}


//////////////////////////
// Nominales Subtyping


// B <= A
// abgeleitet aus Klassendeklaration, in der Literatur als "nominal subtyping" bekannt.
class A {};
class B : public A {};


void h(A x) {}

void g(B x) {}

void testAB() {
  A a;
  B b;

  h(a);     // OK
  h(b);     // OK, weil B <= A
  // g(a);     // NICHT OK
  g(b);     // OK

}

void h2(A* x) {}

void g2(B* x) {}

void testAB1() {
  A* a = new A();
  B* b = new B();

  h2(a);     // OK
  h2(b);     // OK, weil B <= A und daher auch B* <= A*
  // g2(a);     // NICHT OK
  g2(b);     // OK

  delete a;
  delete b;
}


//////////////////////
// Overloading

int funny(int x, int y) {
  return x;
}

char funny(char x, char y) {
  return y;
}

void testFunny() {

  cout << funny(1,2);
  cout << funny('a', 'b');

  // cout << funny(1, 'a');  // Ambiguous, nicht klar welche Instanz wir wollen

}

/*

// Folgendes ist nicht erlaubt, weil
// "functions that differ only in their return type cannot be overloaded"

int funny2() {
  return 1;
}

bool funny2() {
  return true;
}

void testFunny2() {

  bool x = funny2();

  int y = funny2();

}


*/



int main() {
  testFunny();
}

Templates


#include <cstdlib>
#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;



// C++ Templates und Vergleich zu Java generics
/////////////////////////////////////////////////


/////////////////////////////
// Templates

template<typename T>
void mySwap(T& x, T&y) {
  T tmp;

  tmp = x; x = y; y = tmp;
}

void testTmp() {
  int x = 1;
  int y = 2;

  mySwap<int>(x,y); // Instanzierung


  float u = 1.0;
  float v = 2.0;

  mySwap(u,v); // Instanz <float> kann inferriert werden mit Hilfe der Argumente.

}

/*

C++ verwendet "Monomorphisation".

 - Wir jede Instanz dupliziere den Programmcode

 - Vorteil. Effizient, da keine Typecasts
   notwendig sind. Typ-spezifischen Optimierungen etc.

 - Nachteil. Codeduplikation

Hier am Beispiel von oben.
*/

void mySwap_int(int& x, int&y) {
  int tmp;

  tmp = x; x = y; y = tmp;
}

void testTmp_mono() {
  int x = 1;
  int y = 2;

  mySwap_int(x,y);

}



// Weiteres Template Beispiel.

template<typename T>
class Elem {
  T val;
public:
  Elem(T init) {
    val = init;
  }
  void print() {
    cout << "\n" << val;
  }
  void replace(T x) {
    val = x;
  }
};

void testElem() {
  Elem<int> e(1);
  Elem<string> s("Hello");

  e.print();
  e.replace(2);
  e.print();
  s.print();
  s.replace("Hallo");
  s.print();

}

// Beachte.
// Kein Typchecking von templates!
// Nur Typchecking von Code erhalten
// durch Monomorphisierung.

struct Point {
  int x;
  int y;
};


void testElem2() {
  Elem<struct Point> p({1,2}); // (P)

  // p.print();
  // Liefert Typfehler,
  // weil keine "<<" Instanz fuer struct Point!
  // Eigentlich sollte schon (P) einen
  // Fehler liefern (da struct Point

}



// Beachte:
// Templates sind verschieden von "generics" in Java.
// In C++
//  - Monomorpization
// In Java
//  - Generische Uebersetzung

// Java's generisches Uebersetzungs Schema am Beispiel.

// 1. Ersetze alle Typparameter durch void*.
// (In Java wird Object verwendet).

class Elem_G {
  void* val;
public:
  Elem_G(void* init) {
    val = init;
  }
  void print() {
    cout << "\n" << val;
  }
  void replace(void* x) {
    val = x;
  }
};

// Passt alles?

void testElem_G() {
  int i = 1;
  Elem_G e(&i);
  // int* <= void*

  e.print();
  int j = 2;
  e.replace(&j);
  e.print();
}


// Beachte.
// Keine korrekte Behandlung von print.
// Benoetigen "run-time type info"
// im Falle von "<<"



enum TYPE {
  INT,
  BOOL,
  // and so on
};

class Elem_GG {
  void* val;
  enum TYPE t;
public:
  Elem_GG(void* init, enum TYPE ty) {
    val = init;
    t = ty;
  }
  void print() {
    // Entspricht "instanceof" in Java.
    switch (t) {
      case INT: {
    int* p = (int*)(val);
          cout << "\n" << *p;
          break;
      }
      case BOOL: {
    bool* p = (bool*)(val);
          cout << "\n" << *p;
          break;
      }
    }
  }
  void replace(void* x) {
    val = x;
  }
};


void testElem_GG() {
  int i = 1;
  Elem_GG e(&i, INT);
  // int* <= void*

  e.print();
  int j = 2;
  e.replace(&j);
  e.print();

  bool b = true;
  Elem_GG f(&b,BOOL);
  f.print();
}

/*

Zusammengefasst.

Monomorphization in C++
   + Effizient
   + Code Duplikation

Generische Uebersetzung in Java
   + Generischer Code
   +- Typchecks zur Laufzeit sind notwendig

 */



int main() {
  // testAdd();
  testElem();
  testElem_G();
  testElem_GG();
}

Syntax Analyse

Zuerst aber reguläre Ausdrücke.

Reguläre Ausdrücke

// Polymorphie am Beispiel  regulärer Ausdrücke.
//
// R ::= x | R + R | R . R | R* | (R)
//
// 1. Templates zwecks Emulation von parametrischer Polymorphie (aka "generics")
// 2. Overloading
// 3. Subtyping



#include <iostream>
#include <string>
#include <memory>
using namespace std;


class RE_ {
public:
  virtual string show() = 0;     // Quasi eine pretty-print Methode.
  virtual bool nullable() = 0;   // Ueberprueft, ob der regulaere Ausdruck,
                                 // das leere Wort enthaelt.

};

// Zwecks virtueller Methodenauswahl, müssen Objekte via Referenz erreichbar sein.
// Wir verwenden "smart pointers".
typedef shared_ptr<RE_> RE;                            // TEMPLATE


// Epsilon, the empty word.
class Eps : public RE_ {
public:
  string show() { return "eps"; }
  bool nullable() { return true; }
};

// Alphabet symbols = letters.
class Letter : public RE_ {
  char x;
public:
  Letter(char y) { x = y; }
  string show() { return string(1,x); }
  bool nullable() { return false; }
};

// Alternatives.
class Alt : public RE_ {
  RE left;
  RE right;
public:
  Alt(RE l, RE r) { left = l; right = r; }
  string show() {
    string s("(");
    s.append(left->show());                          // OVERLOADING
    s.append("+");
    s.append(right->show());
    s.append(")");
    return s;
  }
  bool nullable() { return (left->nullable() || right->nullable()); }
};


// Concatenation.
class Conc : public RE_ {
  RE left;
  RE right;
public:
  Conc(RE l, RE r) { left = l; right = r; }
  string show() {
    string s("(");
    s.append(left->show());
    s.append(".");
    s.append(right->show());
    s.append(")");
    return s;
  }
  bool nullable() { return (left->nullable() && right->nullable()); }
};

// Kleene star.
class Star : public RE_ {
  RE r;
public:
  Star(RE s) { r = s; }
  string show() {
    string s("(");
    s.append(r->show());
    s.append("*)");
    return s;
  }
  bool nullable() { return true; }
};

// Helpers

// Point to note.
// Co-variant subtyping here!
// shared_ptr<Eps> <= shared_part<RE_>.
RE mkEps() { return make_shared<Eps>(Eps()); }                     // SUBTYPING

RE mkLetter(char x) { return make_shared<Letter>(Letter(x)); }

RE mkAlt(RE l, RE r) { return make_shared<Alt>(Alt(l,r)); }

RE mkConc(RE l, RE r) { return make_shared<Conc>(Conc(l,r)); }

RE mkStar(RE r) { return make_shared<Star>(Star(r)); }


// Beispiele

void test() {

  // (x + eps) . (y*)
  RE r = mkConc(mkAlt(mkLetter('x'), mkEps()), mkStar(mkLetter('y')));

  cout << "\n" << r->show();

  cout << "\n" << r->nullable();

}



int main() {

  test();

}


/*

Beachte.

TEMPLATE (siehe oben).

     shared_ptr sind "generisch" in dem zu verwaltenden Datentyp.

OVERLOADING (siehe oben).

      left->show() entspricht   (*left).show()

      left ist vom Typ RE, RE ist gleich shared_ptr<RE_>

      Der Dereferenzierungsoperator "*" ist überladen.

SUBTYPING (siehe oben).

      make_shared<Eps>(Eps())  ist vom Typ   shared_ptr<Eps>

      Rückgabetyp ist shared_ptr<RE_>

      Beachte,  shared_ptr<RE_> ist verschieden von shared_ptr<Eps>!
      Wieso ist der Programmtext Typkorrekt!?

     Es gilt hier Ko-variantes Subtyping.

      1. Subtyping zwischen Basistype

      Aus der Klassendeklaration leiten wir ab  Eps <= RE_
      D.h ein Objekt der Klasse Eps ist auch
      vom Typ RE_


     2. Subtyping zwischen Typen(konstruktoren) mit Typargument, z.B. shared_ptr<....>

       shared_ptr<Eps> <= shared_ptr<RE_>  weil Eps <= RE_

       D.h. Subtyping Relation zwischen shared_ptr Instanzen richtet sich nach
       der Subtyping Relation zwischen den zugrunde liegenden Basistypen.

       Die Richtung von "<=" bleibt erhalten. Die Subtype Relation verhaelt sich hier ko-variant.

       Gibt es auch kontra-variant?
       Ja, betrachte Coercive Subtyping Beispiel.

int func(float x) {
    if (x < 1.0) return 1;
    return 0;
}


In mathematischer Schreibweise, func hat den Typ float -> int.
Es gilt aber auch

  float -> int  <=   int -> float

 weil int <= float

  In diesem Fall verhaelt sich Subtyping Kontra-variant im Argument und Ko-variant im Resultat.



 */

Top-down parser für reguläre Ausdrücke

// g++ --std=c++11

// Beispiel  regulärer Ausdrücke.
//
// R ::= x | R + R | R . R | R* | (R)
//
//   wobei x ein KLEIN Buchstaben ist.



#include <iostream>
#include <string>
#include <memory>
#include <vector>
using namespace std;


// AST Darstellung.

class RE_ {
public:
  virtual string show() = 0;     // Quasi eine pretty-print Methode.
};

// Zwecks virtueller Methodenauswahl, müssen Objekte via Referenz erreichbar sein.
// Wir verwenden "smart pointers".
typedef shared_ptr<RE_> RE;                            // TEMPLATE


// Epsilon, the empty word.
class Eps : public RE_ {
public:
  string show() { return "eps"; }
};

// Alphabet symbols = letters.
class Letter : public RE_ {
  char x;
public:
  Letter(char y) { x = y; }
  string show() { return string(1,x); }
};

// Alternatives.
class Alt : public RE_ {
  RE left;
  RE right;
public:
  Alt(RE l, RE r) { left = l; right = r; }
  string show() {
    string s("(");
    s.append(left->show());                          // OVERLOADING
    s.append("+");
    s.append(right->show());
    s.append(")");
    return s;
  }
};


// Concatenation.
class Conc : public RE_ {
  RE left;
  RE right;
public:
  Conc(RE l, RE r) { left = l; right = r; }
  string show() {
    string s("(");
    s.append(left->show());
    s.append(".");
    s.append(right->show());
    s.append(")");
    return s;
  }
};

// Kleene star.
class Star : public RE_ {
  RE r;
public:
  Star(RE s) { r = s; }
  string show() {
    string s("(");
    s.append(r->show());
    s.append("*)");
    return s;
  }
};

// Helpers

// Point to note.
// Co-variant subtyping here!
// shared_ptr<Eps> <= shared_part<RE_>.
RE mkEps() { return make_shared<Eps>(Eps()); }                     // SUBTYPING

RE mkLetter(char x) { return make_shared<Letter>(Letter(x)); }

RE mkAlt(RE l, RE r) { return make_shared<Alt>(Alt(l,r)); }

RE mkConc(RE l, RE r) { return make_shared<Conc>(Conc(l,r)); }

RE mkStar(RE r) { return make_shared<Star>(Star(r)); }


/////////////////////////////////////
// Tokenizer

typedef enum {
  EOS,           // End of string
  LETTER,
  OPEN,
  CLOSE,
  CONC,
  ALT,
  KLEENE,
} Token_t;


class Token {
public:
  Token() {}
  Token(Token_t tt) {
    kind = tt;
  }
  Token(Token_t tt, char v) {
    kind = tt; val = v;
  }
  bool eos() {
    return kind == EOS;
  }
  Token_t kind;
  char val;
} ;



class Tokenize {
  string s;
  int pos;
public:
  Tokenize(string s) {
         this->s = s;
     pos = 0;
  }

  Token next() {
    if(s.length() <= pos)
      return Token(EOS);

    while(1) {

       if(s.length() <= pos)
         return Token(EOS);

       switch(s[pos]) {
       case '(': pos++;
     return Token(OPEN);
       case ')': pos++;
     return Token(CLOSE);
       case '+': pos++;
     return Token(ALT);
       case '.': pos++;
     return Token(CONC);
       case '*': pos++;
     return Token(KLEENE);
       case ' ':
     pos++;
     break;
       default:  char ch = s[pos];
             if(ch >= 'a' && ch <= 'z') {
                     pos++;
             return Token(LETTER, ch);
         } else {
           cout << "Unerlaubtes Symbol";
           exit(1);
         }
       }
    }
  } // next

  vector<Token> scan() {
    vector<Token> v;
    Token t;

    do {
      t = next();
      v.push_back(t);
    }
    while(! t.eos());

    return v;
  } // scan
};

// Wrapper class, provide the (current) token.
class Tokenizer : Tokenize {
public:
  Token token;
  Tokenizer(string s) : Tokenize(s) { token = next(); }
  void nextToken() {
    token = next();
  }
};


/////////////////////////////////////
// Top-Down Parser fuer
//
//  R ::= x | R + R | R . R | R* | (R)

//  R ::= T R2
//  R2 ::= . T R2 |
//  T ::= F T2
//  T2 ::= + F T2 |
//  F ::= x | x* | (R) | (R)*

// Beachte.
// "+" bindet staerker als "."
// "Kleene star" wird betrachtet im Fall von "letter" und geklammerten Ausdruecken.
// Deshalb bindet "*" staerker als die anderen Operatoren.


// Newer C++ versions come with "optional".
// We simply build our own and follow the original idea as found in Haskell's Maybe
template<typename T>
class Optional {
  bool b;
  T val;
public:
  Optional() : b(false) {}
  Optional(T v) : val(v), b(true) {}
  bool isJust() { return b; }
  bool isNothing() { return !b; }
  T fromJust() { return val; }
};

template<typename T>
Optional<T> nothing() {
  return Optional<T>();
}

template<typename T>
Optional<T> just(T v) {
  return Optional<T>(v);
}


// Functional style top-down parser.

Optional<RE> parseR(Tokenizer &t);
Optional<RE> parseR2(Tokenizer &t, RE left);
Optional<RE> parseT(Tokenizer &t);
Optional<RE> parseT2(Tokenizer &t, RE left);
Optional<RE> parseF(Tokenizer &t);


//  R ::= T R2
Optional<RE> parseR(Tokenizer &t) {
  Optional<RE> left = parseT(t);
  if(left.isNothing())
    return left;

  return parseR2(t,left.fromJust());
}

//  R2 ::= . T R2 |
Optional<RE> parseR2(Tokenizer &t, RE left) {

    if(t.token.kind == CONC) {
        t.nextToken();

    Optional<RE> right = parseT(t);
    if(right.isNothing())
      return right;

    return parseR2(t, mkConc(left, right.fromJust()));
    }

    return just<RE>(left);
}

//  T ::= F T2
Optional<RE> parseT(Tokenizer &t) {
  Optional<RE> left = parseF(t);
  if(left.isNothing())
    return left;

  return parseT2(t,left.fromJust());
}

// T2 ::= + F T2 |
Optional<RE> parseT2(Tokenizer &t, RE left) {

    if(t.token.kind == ALT) {
        t.nextToken();

    Optional<RE> right = parseF(t);
    if(right.isNothing())
      return right;

    return parseT2(t, mkAlt(left, right.fromJust()));
    }

    return just<RE>(left);
}

//  F ::= x | x* | (R) | (R)*
Optional<RE> parseF(Tokenizer &t) {
  switch(t.token.kind) {
  case LETTER: {
    char ch = t.token.val;
    t.nextToken();

    if(t.token.kind == KLEENE) {
      t.nextToken();
      return just<RE>(mkStar(mkLetter(ch)));
    }
    return just<RE>(mkLetter(ch));
  }
  case OPEN: {
    t.nextToken();
    Optional<RE> re = parseR(t);
    if (re.isNothing())
      return re;

    if (t.token.kind != CLOSE)
      return nothing<RE>();

    t.nextToken();

    if(t.token.kind == KLEENE) {
      t.nextToken();
      re = mkStar(re.fromJust());
    }
    return re;
  }

  default:
    return nothing<RE>();
  } // switch
}



// Beispiele


void display(Optional<RE> r) {
  if(r.isNothing()) {
    cout << "nothing \n";
  } else {
    cout << (r.fromJust())->show() << "\n";
  }
  return;
}


void parse(string s) {
  Tokenizer t = Tokenizer(s);

  auto r = parseR(t);

  // Whole input shall be consumed.
  if (t.token.kind == EOS) {
    display(r);
    }
  else {
    display(nothing<RE>());
  }

}


void test() {

  // (x + eps) . (y*)
  RE r = mkConc(mkAlt(mkLetter('x'), mkEps()), mkStar(mkLetter('y')));

  cout << "\n" << r->show();

}



int main() {

  parse("x.y+z");
  parse("x* + y*");
  parse("x + y *");
  parse("(x+y)*");

  parse("x x");

}

Virtuelle Maschinen

Stack-based (virtual) machines (VM)

Simple stack-based VM

// Simple stack-based VM.

#include <vector>
#include <stack>
#include <iostream>
#include <string>
#include <memory>
using namespace std;


///////////////////////////////////////
// VM code
typedef enum {
  PLUS,
  MULT,
  ONE,
  TWO
} Code_t;

string show(Code_t c) {
  switch(c) {
  case ONE: return "1";
  case TWO: return "2";
  case PLUS: return "+";
  case MULT: return "*";
  }
}

string show(vector<Code_t> cs) {
  string s;
  for(int i=0; i < cs.size(); i++) {
    s = s + show(cs[i]);
    if(i < cs.size() - 1) {
      s = s + " ";
    }
  }
  return s;
}

///////////////////////////////////////
// Expressions
//  - eval (interpreter)
//  - convert to "reverse polish notation"
//        Operands come first
//        Computations require a single stack
class Exp {
public:
  virtual int eval() = 0;
  virtual vector<Code_t>convert() = 0;
};
class IntExp : public Exp {
public:
  int x;
  IntExp(int x) {
    if(x == 1 || x == 2) {
      this->x = x;
    }
    else {
      this->x = 1;
      cout << "\nmust be 1 or 2";
    }
  }
  int eval() { return this->x; }
  vector<Code_t>convert() {
    vector<Code_t> v;
    auto n = x == 1 ? ONE : TWO;
    v.push_back(n);

    return v;
  }
};
class PlusExp : public Exp {
public:
  std::shared_ptr<Exp> left, right;
  PlusExp(std::shared_ptr<Exp> left, std::shared_ptr<Exp> right) {
    this->left = left; this->right = right;
  }
  int eval() { return left->eval() + right->eval(); }
  vector<Code_t>convert() {
    auto v1 = left->convert();
    auto v2 = right->convert();
    v1.insert(v1.end(),v2.begin(),v2.end()); // append v2 to v1
    v1.push_back(PLUS);

    return v1;
  }
};

class MultExp : public Exp {
public:
  std::shared_ptr<Exp> left, right;
  MultExp(std::shared_ptr<Exp> left, std::shared_ptr<Exp>right) {
    this->left = left; this->right = right;
  }
  int eval() { return left->eval() * right->eval(); }
  vector<Code_t>convert() {
    auto v1 = left->convert();
    auto v2 = right->convert();
    v1.insert(v1.end(),v2.begin(),v2.end()); // append v2 to v1
    v1.push_back(MULT);

    return v1;
  }
};

// Helper functions

std::shared_ptr<Exp> newInt(int i) {
  return std::make_shared<IntExp>(i);
}

std::shared_ptr<Exp> newPlus(std::shared_ptr<Exp> left, std::shared_ptr<Exp> right) {
  return std::make_shared<PlusExp>(PlusExp(left,right));
}

std::shared_ptr<Exp> newMult(std::shared_ptr<Exp> left, std::shared_ptr<Exp> right) {
  return std::make_shared<MultExp>(MultExp(left,right));
}


///////////////////////////////////////
// VM run-time
class VM {
    vector<Code_t> code;
  public:
    VM(vector<Code_t> c) : code(c) {}

  void showRunConvert() {
    cout << "\n VM code: " << show(code);
    cout << "\n => " << run();
    cout << "\n Exp: " << convert()->eval();
  }

  int run() {
    stack<int> s;

    for(int i = 0; i < code.size(); i++) {
    switch(code[i]) {
    case ONE:
      s.push(1);
        break;
    case TWO:
      s.push(2);
        break;
    case MULT: {
      auto right = s.top();
      s.pop();
      auto left = s.top();
      s.pop();
      s.push(left * right);
      break;
    }
    case PLUS: {
      auto right = s.top();
      s.pop();
      auto left = s.top();
      s.pop();
      s.push(left + right);
      break;
    }
    } // switch
      } // for

      return s.top();
  } // run

  std::shared_ptr<Exp> convert() {
    stack<std::shared_ptr<Exp>> s;

      for(int i = 0; i < code.size(); i++) {
    switch(code[i]) {
    case ONE:
      s.push(newInt(1));
        break;
    case TWO:
      s.push(newInt(2));
        break;
    case MULT: {
      auto right = s.top();
      s.pop();
      auto left = s.top();
      s.pop();
      s.push(newMult(left,right));
      break;
    }
    case PLUS: {
      auto right = s.top();
      s.pop();
      auto left = s.top();
      s.pop();
      s.push(newPlus(left,right));
      break;
    }
    } // switch
      } // for

      return s.top();
  } // convert


};



///////////////////////////////////////
// Examples

void testVM() {

  {
    vector<Code_t> cs{
      ONE, TWO, TWO, PLUS, MULT
    };


    VM(cs).showRunConvert();
  }

  {
    vector<Code_t> cs{
      ONE, TWO, PLUS, TWO, MULT
    };


    VM(cs).showRunConvert();
  }

}


void testExp() {

  auto e = newPlus(newMult(newInt(1),newInt(2)), newInt(1));

  auto run = [](std::shared_ptr<Exp> e) {
        cout << "\n Exp yields " << e->eval();
        auto vm = VM(e->convert());
        vm.showRunConvert();
  };

  run(e);

}


int main() {

  testVM();

  // testExp();
}



/*


Beispiel:
Wie konvertieren wir Exp in VMCode.


1 * (2 + 1)

entspricht

Mult(Int(1), Plus(Int(2), Int(1)))

=>

[ONE, TWO, ONE, PLUS, MULT]



Zwischenschritte:

(1)

Int(1)

=>

 [ONE]

(2)

Plus(Int(2), Int(1))

=>

 [TWO, ONE, PLUS]


Zusammengefasst. Notation: Wir schreiben "+" fuer Konkatenation. "[...]" entspricht Vektor/Sequenz/Liste.

[ONE] + [TWO, ONE, PLUS] + [MULT]


Wie koennten wir das ganze testen?

z.B.

Property 1:

 Exp => convert => VMCode => convert Exp2

 teste ob Exp.eval == Exp2.eval ist

Property 2:

 VMCode => convert => Exp => convert => VMCode2

 teste ob VMCode.run == VMCod2.run


Generierung von Testeingaben!
Z.B. generiere beliebige Exp Objekte!



 */

Funktionale Programmierung

Themen:

Funktionale Programmierung in C++

/*

Funktionaler Programmierstil

Ist das was neues?

Nein! Siehe Lisp aus den 50er/60er Jahren.

Funktionale Programmierung war immer da.
War aber nie "main stream".




Beispiel:

f = \x -> x + 1

Kommentar:  In der Analysis schreiben wir auch f(x) = x + 1.
            "Lambda" ist hier implizit.


Auswertung von f(1).

Wir schreiben "=>" fuer einen Auswertungsschritt.

    f(1)
=> (\x -> x + 1)   (1)
   ^^^^^^^^^^^^    ^^^^^
    Funktion       Argument

=> (Ersetze Parameter x durch das Argument 1)

   1 + 1

=> 2



Beispiel:

 y = 1

 f = \ x -> x + y
     ^^^^^^^^^^^^^^
     "Lambda" Funktion welche auf eine freie Variable y verweist.
     D.h. im Funktionsrumpf koennen wir auf Variable verweisen,
     welchen in einem auesseren Sichtbarkeitsbereicht
     definiert sind.


 f(1) liefert 2

  weil f(1) =>  x + y wobei gilt x = 1
            =>  1 + y
            =>  1 + 1 weil in y der Wert 1 gespeichert ist
            => 2

  // Wir ueberschreiben den in y gespeicherten Wert mit 2.
  y = 2

  f(1) liefert ???

   Es gibt hier 2 Moeglichkeiten.
    (a) capture by copy oder
    (b) capture by reference.

    Der Unterschied zwischen (a) und (b) bezieht sich auf die Behandlung von freien Variablen.

(1)
   Betrachte

    (a) capture by copy, bedeutet Werte von freien Variable werden kopiert.

   Z.B.

    y = 1
    f = \ x -> x + y   // An der Stelle wir ersetzen effektiv y durch den Wert 1.
                       // Aus der Sicht des Compilers gilt:
                       // f = \x -> x + 1

    y = 2

    f(1) liefert 2
     weil  f(1) => x + y wobei x = 1
                => 1 + y wobei die Annahme (a) gilt, der kopierte Wert von y ist hier 1
                => 1 + 1
                => 2


(2)

    Betrachte

    (b) capture by reference, bedeutet bei der Auswertung des Lambda Ausdrucks,
                              holen wir uns immer den aktuellen Wert der freien Variablen.

    Z.B.

    y = 1
    f = \ x -> x + y   // An der Stelle merken wir uns die Referenz auf die freie Variable y
                       // Aus der Sicht des Compilers, mehr Arbeit ist notwendig.
                       // Wir muessen uns alle freien Variablen merken.

    y = 2

    f(1) liefert 3
      weil   f(1) => x + y wobei x = 1
                  => 1 + y wegen Annahme (b), gilt y ausgewertet liefert 2
                  => 1 + 2
                  => 3


Frage:
  Weshalb benutzt man capture by copy, anstatt direkt einen konstanten Wert einzugtragen?
  Also warum der Umweg über die Variable y?


Antwort:
  Siehe Konstanten. Besser Platzhalter (Variablen) zu verwenden als konkrete Zahlen.


Anmerkung:

Annahme ist "capture by copy".

Beispiel 1.

 y = 1

 f = \ x -> x + y  // P

 y = 2

 g = \x -> x + y

 f(1) => 2   Beachte an der Stelle P hat
             y den Wert 1

 g(1) => 3


Beispiel 2.

 y = 1

 f = \ x -> x + y

 f(1) => 2

 y = 2

 f = \x -> x + y

 f(1) => 3


Beispiel 3. Summenfunktion.

 sum1 = \(x,y) -> x + y

 sum1(2,3) => 5

 sum2 = \x -> (\y -> x + y)

   sum2 hat den Typ int -> (int -> int)

      Anmerkung: Klammern koennen weggelassen werden, Funktionstypen sind rechts-assoziativ.
                 D.h. der Typ int -> int -> int entspricht int -> (int -> int)

                 In C++ Notation, geschrieben als       function<function<int(int)>(int)>

                 Lese function<function<int(int)>(int)> von rechts nach links.

                 Betrachte function<function<int(int)>(int)>

                 1.    function<int(int)> <- int

                 2.   (int <- int) <- int

                 3. Reverse!   Liefert   int -> (int -> int)

 sum2(2,3)  !?
    geht nicht, sum2 erwartet als "ersten" Wert einen int Wert

 sum2(2) entspricht

 (\x -> (\y -> x + y)) (2)
 =>
   \y -> 2 + y


 (sum2(2))(3) entspricht

  ((\x -> (\y -> x + y)) (2)) (3)
 =>
  (\y -> 2 + y) (3)
 =>
   2 + 3
 => 5


  Zu viele Klammern!

  Was ist mit folgendem?

  sum2(x)(y)

  Es gibt zwei Interpretationsmoeglichkeiten

  (a)    (sum2(x))(y)

  (b)    sum2((x)(y))

  Annahme ist, Funktionsapplikation is links-assoziativ.

  Deshalb gilt Interpretation (a).



 */


#include <functional>
#include <stdio.h>
#include <iostream>
using namespace std;



void mapInt(int* p, int len, int f(int)) {
 for(auto i=0; i<len; i++)
   p[i] = f(p[i]);
}


int inc(int x) { return x+1; }
int square(int x) { return x*x; }


int test1() {
   int x[5] = { 1,2,3,4,5 };

   // Funktionen mit Namen
   mapInt(x,5,inc);
   mapInt(x,5,square);

   // Lambdas
   mapInt(x,5, [](int x) -> int { return x+1; });
   mapInt(x,5, [](int x) -> int { return x*x; });

   // Kombination mit auto
   auto f = [](int x) -> int { return x+1; };
   mapInt(x,5,f);

   printf("%d",x[0]);
   return 0;
}

void test2() {

  // Entweder
  // int x = 1;
  // oder wir verwenden "auto".
  auto x = 1;
  auto f = [x] (int y) -> int { return x + y; };

  cout << f(1) << " ";
  x = 5;
  cout << f(1) << " ";

  auto g = [&x] (int y) -> int { return x + y; };

  x = 2;
  cout << g(1) << " ";
  x = 3;
  cout << g(1);
}






int sum(int x, int y) { return x + y; }

void sumEx() {
      cout << sum(5, 2) << endl;
      cout << sum(10, 5) << endl;
}

// Mit lambdas.
void sumEx1() {
      auto sum = [](int x, int y) -> int { return x + y; };

      cout << sum(5, 2) << endl;
      cout << sum(10, 5) << endl;


      // Beachte. Inferenz von Rueckgabe Typ ist moeglich.

      auto sum2 = [](int x, int y) /* -> int */ { return x + y; };

      cout << sum2(5, 2) << endl;
      cout << sum2(10, 5) << endl;

}

// Partial function application.
// Uncurried form. See https://wiki.haskell.org/Currying
// The lambda bound variable x appears in the capture list of the inner lambda.
void sumEx2() {

      // Funktion welche eine Funktion hat Rueckgabewerthat!

      // Betrachte std::function<int(int).
      // Beschreibt eine Funktion mit Argumenttyp int und Rueckgabetyp int.
      // In der Mathematik geschrieben als "int -> int".
      // In C++, ist "function" ein Template zur Beschreibung solcher Funktionsraeume.
      // Das ganze ist definiert im Name space "std".

      function<function<int(int)>(int)> sum;
      //         int -> int
      //  int -> (int -> int) ist der Typ von sum !!!
      // Im allgemeinen gilt:
      //  function<B(A)>  beschreibt den Funktionstyp "A -> B".

      sum = [](int x) -> function<int(int)> { return [x](int y) -> int { return x + y; }; };
      // 1.   Auesserer Lambda-Ausdruck, hat Parameter x vom Typ int
      // 2.                     Rueckgabetyp ist ein Lambda Funktionstyp!
      // 3.                                                    Innerer Lambda-Ausdruck!
      // 4.                                                    Parameter y vom Typ int.




      // Liefert Fehler!
      // Weil sum, d.h. der aeussere Lambda-Ausdruck erwartet "int x" als Parameter!
      // cout << sum(5,2) << endl;

      // cout << sum(5) << endl;
      // Beachte:
      //  Welchen Wert liefert sum(5) ???
      // sum(5) liefert eine Funktion zurueck!
      // Deshalb liefert "cout << sum(5);" einen Compilerfehler.

      // cout << (sum(5))(2) << endl;

      // Wieso erhalten wir 7?
      // 1. sum(5) liefert effektiv den Lambda-Ausdruck "\y -> 5 + y".
      // 2. sum(5) ist eine Funktion, wir fuettern diese Funktion mit einem weiteren Argument.
      // 3.     (sum(5))      (2)
      //        ^^^^^^^^      ^^^^^
      //        Funktion      Argument
      // 4. Deshalb erhalten wir 7 als Ergebnis!

      // Beispiel einer partiellen Funktionsanwendung.

      // ZWISCHENDURCH: In C++/Java gilt folgendes.
      // Im Fall von Methodenaufrufen, betrachte z.B.
      //  (obj.m1()).m2()
      //   obj unterstuert die Methode m1 und liefert ein Objekt zurueck
      //   auf welches wir Methode m2 anwenden.
      //  Abkuerzendende Schreibweise: obj.m1().m2()
      //  Annahme ist, dass Methodenaufrufe sind links-assoziativ.

      //  Obige Annahme fuer Methodenaufrufe wird auch angewandt
      //  fuer Funktionsanwendungen.
      //  Annahme ist, dass Funktionsanwendung sind links-assoziativ.
      //  Deshalb anstatt (sum(5))(2) koennen wir kuerzer schreiben:

      cout << sum(5)(2) << endl;

}

// Spielereien.
void sumEx3() {

  // Maximale Typinferenz!!!
  auto sum = [](int x) /* -> std::function<int(int)> */ { return [x](int y) { return x + y; }; };

  auto inc = sum(1);

  cout << inc(2) << endl;
  // Effektiv das gleiche wie
  // cout << sum(1)(2) << endl;

}

void test3() {
  // sumEx();
  // sumEx1();
  // sumEx2();
  // sumEx3();

}

////////////////////////////////////
// Zaehlen von Woertern.
// Wort = Sequenz von Zeichen welche nicht ' ' (Leerzeichen/Blank) entaehlt.


std::function<string(string)> skip(std::function<bool(char)> p)  {
  return [p](string s) -> string  {
    if (s.length() == 0) {
      return s;
    } else if(p(s[0])) {
    string s2 = s.substr(1);
    return skip(p)(s2);
    } else {
    return s;
    }
  };
}


int count(string input) {
  int cnt = 0;

  auto skipBlanks = skip([](char c) -> bool { return c == ' '; });

  auto scanWord = skip([](char c) -> bool { return c != ' ';});

  while(input.length() != 0) {
    input = skipBlanks(input);
    if(input.length() != 0) {
      cnt++;
      input = scanWord(input);
    }
  }
  return cnt;
}

void testCount() {
  string tests[3] = { "", "dfd ", " dfjkd .s22?? " };

  for(int i=0; i <3; i++) {
    cout << "\n" << count(tests[i]);
  }
}

int main() {
  // test1();
  test2();
  // test3();
  // testCount();
}

Stack machines using some functional abstraction

// Simple stack-based VM.

#include <vector>
#include <stack>
#include <iostream>
#include <string>
using namespace std;

// VM code
typedef enum {
  PLUS,
  MULT,
  ONE,
  TWO
} Code_t;

string show(Code_t c) {
  switch(c) {
  case ONE: return "1";
  case TWO: return "2";
  case PLUS: return "+";
  case MULT: return "*";
  }
}

string show(vector<Code_t> cs) {
  string s;
  for(int i=0; i < cs.size(); i++) {
    s = s + show(cs[i]);
    if(i < cs.size() - 1) {
      s = s + " ";
    }
  }
  return s;
}

class VM {
    vector<Code_t> code;
  public:
    VM(vector<Code_t> c) : code(c) {}

  void showAndRun() {
    cout << "\n Exp: " << exp();
    cout << "\n VM code: " << show(code);
    cout << "\n => " << run();
  }

  void showAndRun2() {
    cout << "\n Exp: "
     << exec<string>(
         [](Code_t x) -> string {
           return show(x);
         },
         [](string x, Code_t op, string y) -> string {
           switch(op) {
               case PLUS: return ("(" + x + " + " + y + ")");
               case MULT: return ("(" + x + " * " + y + ")");
           default: return ""; // should never apply
           }
             });
    cout << "\n VM code: " << show(code);
    cout << "\n => "
     << exec<int>(
         [](Code_t x) -> int {
               switch(x) {
               case ONE: return 1;
           case TWO: return 2;
           default: return -1; // should never apply
           }
         },
         [](int x, Code_t op, int y) -> int {
           switch(op) {
               case PLUS: return (x+y);
               case MULT: return (x*y);
           default: return -1; // should never apply
           }
             });
  }

/*

Methoden run sind exp sind von der Struktur sehr aehnlich.

Wir abstrahieren den gemeinsamen Kern in der exec Methode
welche in einer apply Funktion parameterisiert ist.

Durch geeignet Wahl der apply Funktion erhalten wir run und exp.

*/

  int run() {
    stack<int> s;

    for(int i = 0; i < code.size(); i++) {
    switch(code[i]) {
    case ONE:
      s.push(1);
        break;
    case TWO:
      s.push(2);
        break;
    case MULT: {
      auto right = s.top();
      s.pop();
      auto left = s.top();
      s.pop();
      s.push(left * right);
      break;
    }
    case PLUS: {
      auto right = s.top();
      s.pop();
      auto left = s.top();
      s.pop();
      s.push(left + right);
      break;
    }
    } // switch
      } // for

      return s.top();
  } // run

  string exp() {
    stack<string> s;

      for(int i = 0; i < code.size(); i++) {
    switch(code[i]) {
    case ONE:
      s.push("1");
        break;
    case TWO:
      s.push("2");
        break;
    case MULT: {
      auto right = s.top();
      s.pop();
      auto left = s.top();
      s.pop();
      s.push("(" + left + " * " + right + ")");
      break;
    }
    case PLUS: {
      auto right = s.top();
      s.pop();
      auto left = s.top();
      s.pop();
      s.push("(" + left + " + " + right + ")");
      break;
    }
    } // switch
      } // for

      return s.top();
  } // run

  template<typename T>
  T exec(std::function<T(Code_t)> convert, std::function<T(T,Code_t,T)> apply) {
    stack<T> s;

      for(int i = 0; i < code.size(); i++) {
    switch(code[i]) {
    case ONE:
      s.push(convert(ONE));
        break;
    case TWO:
      s.push(convert(TWO));
        break;
    case MULT: {
      auto right = s.top();
      s.pop();
      auto left = s.top();
      s.pop();
      s.push(apply(left,MULT,right));
      break;
    }
    case PLUS: {
      auto right = s.top();
      s.pop();
      auto left = s.top();
      s.pop();
      s.push(apply(left,PLUS,right));
      break;
    }
    } // switch
      } // for

      return s.top();
  } // run

};


// Examples

void testVM() {

  {
    vector<Code_t> cs{
      ONE, TWO, TWO, PLUS, MULT
    };


    VM(cs).showAndRun();
  }

  {
    vector<Code_t> cs{
      ONE, TWO, PLUS, TWO, MULT
    };


    VM(cs).showAndRun();
  }


}



int main() {

  testVM();
}