for Schleifen

Einstieg
Bereiche ganzzahliger Werte durchlaufen
Bereiche ganzzahliger Werte mit definierter Schrittweite
Nicht Int-Collection
Mehrere Generatoren
Filter

Einstieg

In [38] beschreiben Martin Odersky et al. die Scala for Schleife als das Scala Schweizer Armeemesser der Iteration. "Scala's for expression is a Swiss army knife of Iteration."

Die Scala for Schleife eignet sich nicht nur zum Durchzählen von integralen Werten. Mit Scala for Schleifen ist es möglich, elegant über die Elemente einer Collection zu iterieren. Dieses Kapitel soll die Möglichkeiten der for Schleife in Scala näher beleuchten.


↑↑↑ Seitenanfang ↑↑↑

Bereiche ganzzahliger Werte durchlaufen

Die for - Schleife wird oft dazu verwendet (auch in anderen Programmiersprachen) eine Int - Variable einen bestimmten Wertebereich durchlaufen zu lassen. Das nachfolgende Beispiel zeigt eine for - Schleife, wo eine Variable i die Werte 0 bis 4 annimmt und der aktuelle Wert ausgegeben wird.

for (i <- 0 to 4)
  println(i)          
            

Der Ablauf dieser Schleife führt zu folgender Ausgabe:

0
1
2
3
4          
            

Möchte man, dass der letzte Wert nicht Bestandteil der Schleife ist, kann man folgende Variante der for - Schleife verwenden:

for (i <- 0 until 4)
  println(i)          
            

Der Ablauf dieser Schleife führt zu folgender Ausgabe:

0
1
2
3          
            

Die oben vorgestellten for - Schleifen können den Eindruck hinterlassen, dass es sich bei der Variable i um eine var Variable handelt, da der Wert bei jedem Schleifendurchlauf verändert wird. Dies ist jedoch nicht der Fall. Bei jedem Schleifendurchlauf wird eine neue val Variable für i verwendet. Eine Zuweisung eines neuen Wertes für i ist innerhalb der Schleife nicht möglich.

Der Ausdruck in der hier gezeigten for - Schleife wird als Generator (eng. generator) bezeichnet. <- generiert individuelle Werte aus einer Collection zur Verwendung in einem Ausdruck 1. Rechts von <- steht die Collection, aus der die Werte entnommen werden sollen. Links von <- steht der Name einer val Variablen, auf die wir innerhalb der Schleife zugreifen können. Wenn also <- Elemente aus einer Collection generiert, müssen auch die Ausdrücke 0 to 4 und 0 until 4 als Ergebnis eine Collection liefern, was auch der Fall ist. Die beiden Ausdrücke liefern eine Collection vom Typ scala.collection.immutable.Range (bzw. scala.collection.immutable.Range.Inclusive bei to), welche Int- Elemente in einem Bereich zwischen einem Start- und Endwert bereitstellt. Der Umstand, dass eine Collection in der for-Schleife verwendet wird, führt dazu, dass wir die Collection auch außerhalb des for Ausdruckes definieren können und mithilfe einer Variablen der for Schleife zuführen können.

scala> val myRange = 1 to 3
myRange: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3)

scala> for (i <- myRange) println(i)
1
2
3
timpt.de - X2H V 0.11

In den bisherigen Beispielen haben wir Int Literale verwendet. Wenn wir uns die API Dokumentation ansehen, werden wir feststellen, dass für Int die Funktionen to und until nicht definiert sind. Damit die Funktionen zur Verfügung stehen, wird der Int Wert zunächst (durch impliziete Typumwandlung) in einen scala.runtime.RichInt Typ umgewandelt, für den die Funktionen definiert sind. Die impliziete Typkonvertierung ist in scala.LowPriorityImplicits definiert, welche durch scala.Prefed erweitert wird.

In for - Schleifen, die wir bisher kennengelernt haben, muss nicht unbedingt eine Collection vom Typ Range verwendet werden. Wir können auch problemlos dem Generator eine Collection vom Typ List zur Verarbeitung übergeben.

scala> val myList = List(1,2,3)
myList: List[Int] = List(1, 2, 3)

scala> for (i <- myList) println(i)
1
2
3
timpt.de - X2H V 0.11

↑↑↑ Seitenanfang ↑↑↑

Bereiche ganzzahliger Werte mit definierter Schrittweite

In den bisherigen Schleifen sind wir Int - Werte immer mit einem Inkrement von 1 durchlaufen. Die beiden Methoden to und until sind in RichInt überladen, sodass wir das Inkrement festlegen können.


def to (end: Int, step: Int): Inclusive
def to (end: Int): Inclusive 

def until (end: Int, step: Int): Range 
def until (end: Int): Range           
            

Das nachfolgende Beispiel zeigt eine for Schleife, die Int - Werte mit einem Inkrement von 3 durchläuft.

scala> for (i <- 4 to (17,3)) println(i)
4
7
10
13
16
timpt.de - X2H V 0.11

Die hier gezeigte Vorgehensweise gilt Analog für die Methode until.

Die Int - Werte müssen nicht in aufsteigender Reihenfolge durchlaufen werden, vielmehr können wir auch ein Dekrement (die Werte werden kleiner) als Schrittweite festlegen.

scala> for (i <- 13 until (6,-2)) println(i)
13
11
9
7
timpt.de - X2H V 0.11

↑↑↑ Seitenanfang ↑↑↑

Nicht Int-Collection

Bisher haben wir nur for-Schleifen mit Int-Werten verwendet. Wir können jedoch auch Collections anderer (aller) Typen verwenden. Beispielhaft folgt nachfolgend eine Anwendung der for-Schleife auf eine List mit Strings.

scala> val myList = List("Hello","world","Scala","rocks")
myList: List[java.lang.String] = List(Hello, world, Scala, rocks)

scala> for (s <- myList) println(s)
Hello
world
Scala
rocks
timpt.de - X2H V 0.11

↑↑↑ Seitenanfang ↑↑↑

Mehrere Generatoren

In vielen Fällen möchten wir verschieden Werte verschachtelt durchlaufen. In Scala können wir die Verschachtelung in einer for-Schleife definieren. Beispielsweise können wir zwei Generatoren in einer for-Schleife definieren, die dann verschachtelt durchlaufen werden.

scala> for (i <- 0 to 2; j <- 3 to 4) println(i+" "+j)
0 3
0 4
1 3
1 4
2 3
2 4
timpt.de - X2H V 0.11

Verwenden wir statt der runden Klammern geschweifte Klammern und definieren die Generatoren in zwei Zeilen, können wir auf die Trennung mit einem Semikolon verzichten.

scala> for {i <- 0 to 2
     |      j <- 3 to 4} println(i+" "+j)
0 3
0 4
1 3
1 4
2 3
2 4
timpt.de - X2H V 0.11

↑↑↑ Seitenanfang ↑↑↑

Filter

Nicht immer möchten wir alle Werte einer Collection in einer for-Schreife verarbeiten. Zu diesem Zwecke stehen uns in for-Schleifen sogenannte Filter zur Verfügung. Ein Filter wird dabei einfach durch einen if-Ausdruck innerhalb des for-Ausdrucks definiert. Liefert der Filter das Ergebnis true wird der aktuelle Schleifendurchlauf bearbeitet, liefert der Filter false wird der aktuelle Schleifendurchlauf nicht bearbeitet.

Im nachfolgenden Beispiel wenden wir eine for Schleife auf einen Range mit den Werten 1 bis 10 (inklusive) an und geben nur die geraden Werte auf der Systemausgabe aus.

object ForLoop {
  def main(args: Array[String]): Unit = {
    for {i <- 1 to 10
         if (i % 2 == 0)} println(i)
  }
}
timpt.de - X2H V 0.17

Die Ausführung des Programms führt zu folgender Ausgabe auf der Systemausgabe:

2
4
6
8
10

timpt.de - X2H V 0.17

Zugegeben, das gleiche hätten wir auch mit einer entsprechenden Range-Definition erreichen können, was aber nicht Inhalt des Abschnittes ist.

Wie bei den Generatoren sind wir bei Filter nicht auf einen einzigen if-Ausdruck beschränkt und können mehrere Filter definieren. Im nachfolgenden Beispiel verwenden wir zwei Generatoren und zwei Filter. Zur Ausführung des Schleifeninhaltes kommt es im Beispiel nur, wenn die Variable i gerade und die Variable j ungerade ist.

object ForLoop2 {
  def main(args: Array[String]): Unit = {
    for {i <- 1 to 6
         if (i % 2 == 0)
         j <- 1 to 6
         if (j % 2 != 0)} println(i+" "+j)
  }
}
timpt.de - X2H V 0.17

Die Ausführung des Programms führt zu folgender Ausgabe auf der Systemausgabe:

2 1
2 3
2 5
4 1
4 3
4 5
6 1
6 3
6 5
timpt.de - X2H V 0.17

Die Filter haben keine bestimmte Position innerhalb des for-Ausdrucks. Greifen Filter aber auf Variable zu, die im for-Ausdruck definiert werden, müssen diese nach der Variablendefinition positioniert werden. Dementsprechend hätten wir unser obiges Beispiel auch folgendermaßen schreiben können.

object ForLoop2 {
  def main(args: Array[String]): Unit = {
    for {i <- 1 to 6         
         j <- 1 to 6
         if (i % 2 == 0)
         if (j % 2 != 0)} println(i+" "+j)
  }
}
timpt.de - X2H V 0.17


____________
1: Vergleiche [39] S. 60
↑↑↑ Seitenanfang ↑↑↑