Der ewige Kampf gegen Redundanzen

C (1973), Modula-2 (1978) und Ada (1980) führten daher die Möglichkeit ein, eine beliebige Schleife durch eine spezielle Sprunganweisung abzubrechen. Insbesondere kann man so auch eine Endlosschleife bedingt abbrechen. Damit wäre die obige Aufgabe redundanzfrei lösbar (C-Syntax, Listing 5).

Listing 5
int summe=0, x;
for(;;){
    lesen(&x);
    if (x

Das entspricht dem allgemeinen Muster zur Verarbeitung einer im Vorhinein nicht bekannten Anzahl an Datensätzen in einer mittelprüfenden Schleife:

Initialisierung
for(;;){
  Bereitstellung
  if (nichtErfolgreich) break;
  Verarbeitung
}

Rekursion: Wenn eine Funktion sich selbst direkt oder indirekt aufruft, wird das Rekursion genannt. Diese Möglichkeit wurde in Lisp und Algol parallel eingeführt. Die höchst verbreiteten Sprachen Fortran und Cobol erlaubten es jedoch nicht. Rekursion erlaubt sehr elegante Formulierungen von Algorithmen, zum Beispiel zur Traversierung von Bäumen. Einen direkten Beitrag zur Redundanzvermeidung leistet sie jedoch nicht. Wenn der rekursive Aufruf die letzte Anweisung einer Funktion ist, kann der Compiler ihn durch eine einfache, effiziente Schleife ersetzen. So wird das in den funktionalen Sprachen umgesetzt.

Eigene Steuerstrukturen definieren

Die Möglichkeit, eigene Steuerstrukturen zu definieren, wurde in Lisp (1958) geboren, da dort Programmcode und Daten in gleicher Form, den so genannten S-Ausdrücken, notiert wurden. Zum Beispiel bedeutet (TIMES N 7) einerseits die Multiplikation von N mit 7. Andererseits ist es einfach eine Liste mit den Elementen TIMES, N und 7. Eine in Interlisp mittels DF definierte Funktion evaluierte die ihr übergebenen Argumente nicht vor der Ausführung ihres Funktionsrumpfes, sondern überließ das expliziter Benutzung der Funktion EVAL im Funktionsrumpf. So konnte ein Funktionsrumpf die Anzahl und Reihenfolge der Berechnung der Argumente steuern.

Als Liebhaber redundanzfreier Lösungen vermisste ich in der modernen objektorientiert-funktionalen Sprache Scala (2001) eine mittelprüfende Schleife, konnte sie aber selbst definieren (Listing 6). Schlüsselwörter darin sind def, var und while.

Listing 6
def loopGetTestProcess[Item]
    (getItem: => Item)
    (shouldContinue: Item=>Boolean)
    (processItem: Item=>Unit)
{
    var item = getItem
    while(shouldContinue(item)){
        processItem(item)
        item = getItem
    }
}

Die Funktion loopGetTestProcess hat drei Parameterlisten. In der ersten wird ein Ausdruck getItem zum Bereitstellen eines neuen Elements erwartet, in der zweiten eine boolesche Funktion shouldContinue zum Beurteilen des Erfolgs der Bereitstellung und in der dritten eine Funktion processItem zur Verarbeitung eines Elements. Durch den generischen Parameter [Item] wird gesichert, dass die Typen zusammenpassen. Die im Funktionsrumpf enthaltene Redundanz des Aufrufs von getItem ist gut gekapselt und bei der Verwendung nicht mehr sichtbar. Als syntaktischer Zucker kann eine Parameterliste der Länge 1 auch mit geschweiften Klammern geschrieben werden, sodass zum Beispiel folgende Verwendung möglich ist:

def printLines(in: java.io.BufferedReader){
    loopGetTestProcess(in.readLine())(_!=null){
        println
    }
}

In Java würde es unter Benutzung von break; wie in Listing 7 aussehen. Die große Errungenschaft in Scala ist, dass der Programmierer diese Steuerstruktur selbst definiert hat und auch ganz andere definieren kann.

Listing 7
void printLines(final java.io.BufferedReader in){
    for(;;){
      final String line = in.readLine();
      if(line==null)break;
      println(line);
    }
}
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.