Code wiederholen mit Schleifen – Programmieren lernen mit JavaScript – Thytos
Nächstes Video startet in 3 Sekunden.
Programmieren lernen mit JavaScript

Code wiederholen mit Schleifen

Um Lis­ten zu ver­ar­bei­ten, werden sie Ele­ment für Ele­ment ab­ge­gan­gen. In der Re­gel wer­den da­bei die glei­chen Be­feh­le für je­des Ele­ment aus­ge­führt. Für die­se Fäl­le gibt es ei­ge­ne Kon­troll­struk­tu­ren: Schleifen.

In der Datenverarbeitung spielen Listen eine zentrale Rolle. Ob es eine Liste von Beiträgen im persönlichen Blog ist, eine Gliederung von Personenkarteien in einer Datenbank oder einem Register von Eventhandlern im Code – überall sind Listen von Bedeutung.

Um mit Listen zu arbeiten, werden die Listen-Elemente nacheinander schrittweise verarbeitet, denn es gibt in der sequentiellen Programmierung keine Möglichkeit, alle Listen-Elemente gleichzeitig zu verarbeiten.
Dabei wird der gleiche Befehlssatz auf jedes einzelne Element angewendet. Dieser Vorgang heißt iterieren, im Englischen iterate, und kommt aus dem Lateinischen von iterare, wiederholen.

Beispielfall: Array zu String zusammenfassen

Das folgende Beispiel zeigt eine Variable, der ein Array zugewiesen ist, das die Namen der Besetzung von Das Schweigen der Lämmer enthält.

var cast = ["Anthony Hopkins", "Lawrence A. Booney", "Kasi Lemmons", "Jodie Foster"];

cast.length
// => 4

Die Namen des Arrays sollen nun in einem String zusammengefasst werden, wobei zwischen den Namen ein Komma mit Leerzeichen als Trennung stehen soll.
Dazu kann eine Variable erstellt werden, um den resultierenden String zu beherbergen. Jedes Element des cast-Arrays wird dann dem String angehängt, zusammen mit der gewünschten Trennung.

var resString = ""; // Result String beherbergt das Ergebnis
resString += cast[0] + ", ";
resString += cast[1] + ", ";
resString += cast[2] + ", ";
resString += cast[3] + ", ";

Wie zu erkennen ist, wird derselbe Befehl wiederholt und für jedes Array-Element aufgerufen. Statt das manuell auszuführen, kann eine Iterator-Funktion geschrieben werden, die das rekursiv tut.

resString = ""; // resString zurücksetzen

function iterator(i) { // Index als Parameter
  if (i < cast.length) { // Abbruchbedingung
    resString += cast[i] + ", ";
    iterator(++i); // Inkrement-Operator
  }
}

Statt den Index der Stelle, von der ein Name aus dem Array ausgelesen werden soll, fest in den Code zu schreiben, wird i verwendet, ein variabler Parameter.
Die iterator-Funktion wird mit ++i anschließend erneut aufgerufen. ++i ist der Inkrement-Operator und zählt i um eins hoch.
Die Abbruch-Bedingung zu Beginn der Funktion sorgt dafür, dass die Rekursion nicht endlos ausgeführt wird.

Wird iterator mit 0 als initialen Parameter aufgerufen, enthält resString anschließend den korrekten Wert.

iterator(0);

resString;
// => "Anthony Hopkins, Lawrence A. Booney, Kasi Lemmons, Jodie Foster, "

Was genau ist hier passiert?

  • iterator wird mit 0 aufgerufen
  • In der if-Bedingung wird geprüft, ob i < cast.length, also 0 < 4, ist, was stimmt, weshalb der bedingte Code ausgeführt wird
  • An der Stelle cast[0] befindet sich "Anthony Hopkins". Dieser String wird zusammen mit ", " dem resString hinzugefügt, sodass der resString anschließend lautet:
    "Anthony Hopkins, "
    
  • Danach wird ++i gerechnet. Da i gleich 0 ist, kommt dabei 1 heraus. Mit dieser 1 wird iterator erneut aufgerufen.
  • Wieder am Anfang der iterator-Funktion wird erneut geprüft, ob i < cast.length ist. Da i nun 1 ist, wird also geschaut, ob 1 < 4 ist. Da das stimmt, wird der Code-Block ausgeführt.
  • An der Stelle cast[1] befindet sich "Lawrence A. Booney", der mit der Trennung zu "Lawrence A. Booney, " kombiniert und dem resString hinzugefügt wird. Der resString lautet anschließend:
    "Anthony Hopkins, Lawrence A. Booney, "
    
  • ++i zählt 1 hoch zu 2 und ruft damit iterator erneut auf.
  • … Dieser Prozess wiederholt sich solange, bis iterator(4) aufgerufen wird. Mit i gleich 4 ist die Abbruchbedingung nicht erfüllt, denn cast.length ist ebenfalls 4 und i < cast.length, also 4 < 4, ist nicht wahr.
  • Deswegen wird von dort aus in die aufrufende Funktion zurückgekehrt (iterator(3)) und der weitere Code dort ausgeführt – welcher allerdings nicht vorhanden ist, weswegen auch hier die Funktion beendet wird und zum Aufrufer zurückkehrt (iterator(2)) und so weiter, bis der Callstack vollständig abgearbeitet ist.

Der ganze Prozess kann zu einer eigenen Funktion namens join zusammengefasst werden, die ein beliebiges Array und den String, der als Trennung gelten soll, als Parameter annimmt.

// join nimmt beliebige Arrays an und fügt beliebige delimiter ein
function join(arr, delimiter) {
  var resString = "";

  function iterator(i) {
    if (i < arr.length) {
      // Statt cast wird der arr-Parameter genutzt
      resString += arr[i];
      if (i < arr.length - 1) {
        // Mit einer zusätzlichen if-Bedingung wird ein überstehendes Trennzeichen vermieden
        resString += delimiter; // delimiter-Parameter statt festem ", "
      }
      iterator(++i);
    }
  }
  iterator(0);

  return resString;
}

// Test:
join(cast, "|")
// => "Anthony Hopkins|Lawrence A. Booney|Kasi Lemmons|Jodie Foster"

Die join-Funktion funktioniert und nimmt nun beliebige Trennungen ein, sodass das Komma mit Leerzeichen nicht mehr hart gecodet ist. Eine weitere if-Bedingung sorgt dafür, dass eine überflüssige Trennung am Ende des resString vermieden wird.

Spezifische Kontrollstrukturen: Schleifen

Ihr habt nun das Prinzip des Iterierens verstanden und gesehen, dass es möglich ist, dies mit Hilfe von Rekursionen umzusetzen.
Es gibt neben Rekursionen auch dedizierte Steuerkonstrukte für die Wiederholung von Code: Schleifen.

Es gibt verschiedene Arten von Schleifen. Eine davon ist die While-Schleife.
While ist Englisch und bedeutet während oder auch solange. Die While-Schleife führt bestimmten Code unaufhörlich aus, solange eine gegebene Bedingung erfüllt ist.

while (/* Bedingung */) {
  /* Auszuführender Code */
}

Die geschweiften Klammern um den Schleifen-Körper sind nicht notwendig, wenn er nur aus einer einzelnen Anweisung besteht. Es empfiehlt sich allerdings aus Konsistenz- und Lesbarkeits-Gründen immer geschweifte Klammern zu setzen.

Die join-Funktion von oben kann so umgeschrieben werden, dass die Iteration mit einer while-Schleife durchgeführt wird.

function join(arr, delimiter) {
  var resString = "";
  var i = 0; // i ist kein Parameter mehr, sondern eine Variable

  // Die Abbruchbedingung ist die Schleifen-Bedingung:
  while (i < arr.length) {
    // Der Code der iterator-Funktion kann größtenteils übernommen werden
    resString += arr[i];
    if (i < arr.length - 1) {
      resString += delimiter;
    }
    // Da die Funktion selbst nun aber nicht mehr existiert,
    // kann sie auch nicht rekursiv aufgerufen werden.
    // Das ++i steht deshalb nun einzeln:
    ++i;
  }

  return resString;
}

// Test
join(cast, " $$ ")
// => "Anthony Hopkins $$ Lawrence A. Booney $$ Kasi Lemmons $$ Jodie Foster"

Es gibt eine Schleifenart, die für das Hochzählen einer Iterationsvariable ausgelegt ist. Welche das ist und wie sie eingesetzt wird, erfahrt ihr im nächsten Artikel.