/**
 *  @file Beispiel1.java 
 *  Anhand des nachfolgenden Beispiels werden die 
 *  Grundbegriffe 
 *  imperativer Programmiersprachen am Beispiel von
 *  Java eingeführt.
 *
 *  <b>IMPERATIVE PROGRAMMIERSPRACHEN</b> bieten eine
 *   Abstraktion des
 *  von Neumann'schen Rechnerorganisationsmodells,
 *   um die Implementierung
 *  von Algorithmen zu erleichtern.
 *   Die Grundbegriffe der imperativen
 *  Programmierung sind
 *
 *  <ul>
 *    <li> <b>Variablen</b> - dies sind
 *         Abstraktionen von Speicherzellen, welche die
 *          im Algorithmus verarbeiteten Daten enthalten:
 *          Der <b>Variablenname</b>
 *          abstrahiert die Speicheradresse,
 *          der <b>Variablenwert</b> den Dateninhalt
 *          der adressierten Speicherzelle(n).
 *
 *    <li> <b>Anweisungen</b> - dies sind Abstraktionen
 *          der Maschineninstruktionen, welche
 *          Speicherplatz für Variablen reservieren,
            Variablenwerte auswerten, umrechnen und verändern.
 *  </ul>
 *
 *  Zur Erzeugung von Variablen und zur Gruppierung 
 *  zusammengehöriger Anweisungen 
 *  verwendet man
 *
 *  <ul>
 *    <li> <b>Deklarationen</b> - dies sind Festlegungen
 *           oder Vereinbarungen für 
 *          die Verwendung  benutzerdefinierter Namen,
 *          die Klassen, Methoden oder Variablen 
 *          repräsentieren.
 * </ul>
 *
 *  Weitere Details werden anhand des nachfolgenden
 *  Beispielcodes erläutert.
 */

/** Die <b>Klassendeklaration</b>
 *
 *  Anweisungsfolgen werden in Java in <b>Methoden</b>
 *  zusammengefasst. Methoden
 *  sind einzeln ausführbare Programmeinheiten, 
 *  die in <b>Klassen</b> gruppiert werden.
 *  Die Betrachtung des Klassenbegriffs aus 
 *  objekt-orientierter Sicht erfolgt später -
 *  zunächst spielt hier für uns nur eine Rolle, 
 *  dass der Compiler (javac) Klassen als
 *  kleinste Übersetzungseinheiten erkennt.
 *
 *  Eine Klasse wird mit dem Schlüsselwort
 *  <code>class</code> deklariert und erhält 
 *  einen eindeutigen Namen; die Bestandteile
 *  einer Klasse werden innerhalb von
 *  geschweiften Klammern <code>{ ... }</code> 
 *  gruppiert. (Die präzise Beschreibung der
 *  Grammatik mit Hilfe der erweiterten 
 *  Backus-Naur-Form EBNF erfolgt später).
 *
 *  Die Schlüsselworte <code>public, static</code>
 *  werden zu einem späteren Zeitpunkt 
 *  erläutert.
 */
public class Beispiel1 {


    /**
     *  Jedes Java-Programm muss eine Methode enthalten 
     *  die als
     *  <code> public static void main(String[] args)</code>
     *  deklariert ist.
     *  Diese Deklaration definiert die Startadresse der
     *  ersten Anweisung,
     *  die bei Programmstart auszuführen ist. 
     *
     *  Bei Programmstart werden intern, d.h. ohne dass
     *  spezielle Anweisungen 
     *  im Java-Programmcode erforderlich sind,
     *  folgende Instruktionen
     *  ausgeführt:
     *
     *  <ul>
     *   <li> Die Java-Laufzeitumgebung (virtuelle Maschine)
     *         wird initialisiert
     *        (Details hierzu später)
     *   <li> Die optionalen Zusatzparameter
     *        <code>p1, p2, ...</code>, welche im Aufruf 
     *        <code>java Beispiel1 p1 p2 ...</code>
     *        verwendet wurden, werden in die
     *        <b>Array-Variable</b> <code>args</code> kopiert.
     * </ul>
     *
     * Methoden bestehen aus Variablendeklarationen 
     * (sog. <b>lokale Variablen</b>
     * der Methode) und Anweisungsfolgen. 
     * Variablendeklarationen führen beim Methodenaufruf
     * zur automatischen
     * Ausführung von Instruktionen, welche die
     * <b>Variablendefinition</b>, d.h. die
     * Allokation des Speicherplatzes für die Variable auf
     * dem Stack (Erläuterung
     * des Stack erfolgt später) vornehmen: 
     * Der Speicherplatz für lokale Variablen
     * wird also nicht <b>statisch</b> zur Übersetzungszeit,
     * sondern <b>dynamisch</b> zur
     * Laufzeit, d.h. bei Aufruf der Methode, angelegt.
     */
    public static void main(String[] args) {

        /** <b>Variablendeklarationen</b>
	 *
	 * Java kennt zwei Typen von Variablen:
	 * 
	 * <ul>
	 *   <li> <b>Variablen von Basistypen</b> 
	 *            für ganzzahlige Variablen, 
	 *            Gleitkommavariablen, 
	 *         Boolesche Variablen
	 *         (Wertebereich { true, false })
	 *   <li> <b>Referenzvariablen</b>, welche die 
	 *       <b>Adressen (=Referenzen) von Objekten</b>
	 *        enthalten.
	 * </ul>
	 *
	 * Jede Variablendeklaration erfolgt in der Form
	 * <code>Typname Variablenname</code>, 
	 * die Syntaxrestriktionen für Variablennamen werden
	 * später im Detail besprochen.
	 *
	 * Java bietet folgende Basistypen:
	 *
	 */
	int n; /** Ganzzahlige Variable, z.B., -5, +4, 7, 32Bit */
	float x; /** Gleitkommazahl, z.B. 1.4, 32 Bit, 1E-2 */
	double z;/** Gleitkommazahl, z.B. 1.4, 64 Bit */
	boolean b; /** Boolesche Variable */

        /** Anweisungen gliedern sich in
	 *   <ul>
	 *    <li> <b>Zuweisungen</b>: Verändern Variablenwerte
	 *    <li> <b>Kontrollstrukturen</b>: Steuern die Sequenz
	 *      der Anweisungsausführung
	 *    <li> <b>Ein-/Ausgabeanweisungen</b>: Übertragung von
	 *       Daten zwischen  dem Programm und den Peripheriegeräten
	 *       (Tastatur, Bildschirm, Maus, Platte, ...)
	 *   </ul>
	 */

        /** <b>Zuweisungen</b> werden in der Form
	 *  <code>Zielvariable = Ausdruck;</code>
	 *  notiert. Die Zielvariable definiert durch
	 *  ihren Namen den Ort im Speicher, an welchen 
	 *  der Wert des Ausdrucks geschrieben werden soll.
	 *  Das `=' hat die Bedeuting einer STORE-Instruktion.
	 *  Da Java eine getypte Programmiersprache ist,
	 *  muss der Ausdruck zum Typ der Zielvariablen
	 *  auswerten.
	 *  <b>Ausdrücke</b> bestehen aus Konstanten, Variablen oder
	 *  syntaktisch zulässigen Kombinationen von <b>Operanden</b>
	 *  und <b>Operatoren</b>. Die Operanden können wieder 
	 *  Konstanten oder Variablen oder geschachtelte Ausdrücke
	 *  weiterer Operanden und Operatoren in Klammern <code>(...)</code>
	 *  sein.
	 */
	n = 10; 
	n = n + 1; 
        n = ( n +1 ) * n;

        /** Ausgabeanweisung: (Eingabeanweisungen 
	 *  werden später eingeführt)
	 */
        System.out.println("n = " + n);

	System.out.println("Erster Aufrufparameter: " 
                               + args[0]);




        /**
	 * <b>Arrays</b> sind indizierte Variablen: 
	 *  Mit einem Namen werden
	 * so viele Variablen eingeführt, wie der 
	 * Indexbereich vorgibt. 
	 *  Arrays <code>Typ a</code>
	 * mit Wertebereich <code>{0..n-1}</code>
	 * realisieren mathematische Funktionen 
	 * 
	 *  <code>
	 *   a : {0..n-1} ---> Typ
	 * </code> 
	 *
	 * Array-Definition zur Laufzeit: "Lege
	 * 2 Speicherplätze der Größe int (32Bit) 
	 * zur Speicherung von a[0] bzw. a[1] an.
	 * Ist die Länge der Arrays mit n angegeben, so 
	 * ist der zulässige Indexbereich 0..(n-1).
	 *
	 * Mehrdimensionale Arrays und andere Grenzdefinitionen
	 * werden später besprochen.
	 */
	int a[] = new int[2]; 

	a[0] = 1; 
	a[1] = 2;
	System.out.println("Array-Elemente: " 
                           + a[0] + " " + a[1]);


        /**
	 * Arrays können bei ihrer Definition mit Werten vorbelegt
	 * werden. Dabei wird die Größe automatisch ermittelt
	 * (5 in diesem Beispiel)
	 */
        float f[] = { 1.0f, 3.6f, 7.1f, 4.4f, 5.5f };

        boolean b2;
        b2 = true;
	b = false;

        /** Boolsche Operatoren sind 
	 *  and (&&), or (||), not (!)
	 */
        b = b && b2; 

        /** Ausdruecke ueber 
	 *  Vergleichsoperatoren werten zum Typ boolean aus.
	 *  Vergleichsoperatoren sind <, <=, >, >=, !=, ==
	 */
        b = f[4] < f[0]; 
        b = (f[0] == f[1]);


        /**
	 * Kontrollstrukturen sind
	 *  <ul>
	 *  <li> <code>if () { ... } else { .... }</code>
	 *  <li> <code>while ()</code>
	 *  <li> <code>;</code>  (Anweisungsfolge)
	 *  
	 *  <li> <code>for () { ... } </code>
	 *  <li> <code>do { ... } while ();</code>
	 *  <li> <code>switch() { }</code>
	 * </ul>
	 *
	 */
        if ( f[0] == f[1] ) {
	    /**
	     * if-Zweig
	     * Diese Anweisungsfolge wird ausgeführt,
	     * wenn die Boolesche Bedingung zu true
	     * auswertet
	     */
	    n = n +1;

	}
	else { /** "else-Zweig"
		* Falls (f[0] == f[1]) == false
		*/
            n = n + 2;

	    /**
	     * Alle Kontrollstrukturen können
	     * geschachtelt auftreten:
	     * Hier oder im if-Block kann eine beliebige weitere
	     * Kontrollstruktur auftreten
	     */

	}
 
        /**
	 * Hier geht es weiter, sobald if- oder
	 * else-Zweig ausgefuehrt wurden
	 *
	 * Die <code>switch(Ausdruck) { ... }</code> Anweisung 
	 * bietet die Möglichkeit, if-Bedingungen über Vergleiche
	 * mit ganzzahligen Werten auf elegantere Weise zu formulieren.
	 * <code>Ausdruck</code> muss dabei zu einer ganzen Zahl auswerten.
	 * Zwischen  <code>case Wert:</code> und <code>break;</code> stehen die Anweisungen,
	 * welche auszuführen sind, falls <code>Ausdruck</code> zu <code>Wert</code> auswertet.
	 * Ist <code>Ausdruck</code> mit keinem der  <code>Werte</code> identisch, 
	 * werden die Anweisungen 
	 * des <code>default:</code>-Zweiges ausgeführt.
	 */
	switch(n - 1) {

	    case 1:
                System.out.println("n-1 == 1");
		break;

	    case 2:
                System.out.println("n-1 == 2");
		break;

	    default:
		System.out.println("n-1 hat Wert " + (n - 1));
		break;
	}








	/**
	 * Die <code>while ( b ) { ... }</code> Schleife
	 * führt den <b>Schleifenkörper</b> <code>{ ... }</code> 
	 * wiederholt aus, so lange die Boolsche Bedingung b zu true
	 * auswertet. Falls <code>b == false</code> bei Erreichen
	 * der while-Schleife, wird der Schleifenkörper 
	 * überhaupt nicht durchlaufen.
	 */
        int i;
	i = 0;
        while ( f[i] < f[i+1] && i < 4 ) {

	    System.out.println("f["+i+"] = " + f[i]);
            i = i + 1;

	}

        /**
	 * Die <code>do { ... } while ( b );</code> Schleife
	 * führt den <b>Schleifenkörper</b> <code>{ ... }</code> 
	 * wiederholt aus, so lange die Boolsche Bedingung b zu true
	 * auswertet. Da die while-Bedingung am Ende des 
	 * Schleifenkörpers ausgewertet wird, wird dieser mindestens
	 * einmal durchlaufen. 
	 *
	 * @note Das hier gezeigte Beispiel ist besser für die
	 * while-Schleife als für die do-while-Schleife geeignet. 
	 * Das sieht man bereits daran, dass im Schleifenkörper
	 * der do-while-Schleife eine if-Bedingung auftritt, die mit
	 * der Schleifenbedingung identisch ist.
	 */
	i = 0;
        do {
	    b = f[i] < f[i+1];
	    if ( b ) {
		System.out.println("f["+i+"] = " + f[i]);
	    }
	    i = i + 1;
        } while ( b ) ;


        /**
	 * Die 
	 *   <code>
	 *           for (Index-Initialisierung;
	 *                Schleifenbedingung;
	 *                Indexinkrementausdruck) { ... }
	 *      </code> 
	 * Schleife
	 * führt den Schleifenkörper <code>{ ... }</code> 
	 * wiederholt aus, so lange die Schleifenbedingung zu true
	 * auswertet. Im Prinzip können hier beliebige
	 * Bedingungen wie bei den while-Schleifen verwendet werden;
	 * typischweise verwendet man aber for-Schleifen dann,
	 * wenn eine ganzzahlige Zählvariable verwendet wird,
	 * deren Größe über den Abbruch der Schleife entscheidet.
	 * Im Indexinkrementausdruck können ebenfalls beliebige
	 * Anweisungen stehen, man verwendet ihn aber üblicherweise
	 * zum inkrementieren oder dekrementieren des Schleifenindex.
	 */        
        for ( int j=0; j < 4 && f[j] < f[j+1]; j = j + 1 ) {

	    if ( f[j] < f[j+1] ) {
	       System.out.println("f["+j+"] = " + f[j]);
            }

	}

    }

}
