Go 3: Kontrollstrukturen: Verzweigungen und Schleifen
Programme bestehen aus Daten und Logik. Im ersten und zweiten Teil haben wir primitive und zusammengesetzte Datentypen kennengelernt. In diesem Teil wollen wir diese Daten mithilfe von Verzweigungen und Schleifen verarbeiten.
Go kennt nur relativ wenige Schlüsselwörter
(Spec), 25 um genau zu sein. In diesem Teil
werden Sie zehn davon kennenlernen, nämlich (in alphabetischer Reihenfolge)
break
, case
, continue
, default
, else
, fallthrough
, for
, if
,
range
, und switch
‒ goto
lassen wir weg. Dementsprechend ist dieser
Kursteil auch anhand dieser Schlüsselwörter organisiert.
Verzweigungen (Branching)
Go kennt zwei Arten der Verzwegungen: if
/else
und switch
/case
. Die
Funktionsweise ist vergleichbar mit den entsprechenden Konstrukten aus anderen
Programmiersprachen, jedoch gibt es auch einige wichtige Unterschiede.
if
/else
Ein bedingter Codeblock kann mit if
(Spec) umgesetzt werden, wobei die
Bedingung nicht in Klammern stehen muss:
maxPoints := 100.0
scored := 75.0
ratio := scored / maxPoints
if ratio > 0.8 {
fmt.Println("a ratio of", ratio, "is excellent")
}
if ratio > 0.6 {
fmt.Println("a ratio of", ratio, "is good")
}
Es wird immer ein Codeblock benötigt, der auf der gleichen Zeile wie die Bedingung anfangen muss.
Ausgabe:
a ratio of 0.75 is good
Alternativbedingungen können mit else
formuliert werden. Mehrere
Alternativbedingungen lassen sich mittels else if
definieren:
if ratio > 0.8 {
fmt.Println("a ratio of", ratio, "is excellent")
} else if ratio > 0.6 {
fmt.Println("a ratio of", ratio, "is good")
} else if ratio > 0.4 {
fmt.Println("a ratio of", ratio, "is acceptable")
} else {
fmt.Println("a ratio of", ratio, "is poor")
}
switch
/case
Oftmals wird dieselbe Variable in allen Bedingungen mit einem konstanten Wert verglichen:
if grade == 6 {
fmt.Println("very good")
} else if grade == 5 {
fmt.Println("good")
} else if grade == 4 {
fmt.Println("sufficient")
} else if grade == 3 {
fmt.Println("insufficient")
} else if grade == 2 {
fmt.Println("bad")
} else if grade == 1 {
fmt.Println("very bad")
}
In diesem Fall ist ein switch
/case
-Konstrukt
(Spec) besser lesbar:
switch grade {
case 6:
fmt.Println("very good")
case 5:
fmt.Println("good")
case 4:
fmt.Println("sufficient")
case 3:
fmt.Println("insufficient")
case 2:
fmt.Println("bad")
case 1:
fmt.Println("very bad")
}
Im Gegensatz zu anderen Programmiersprachen aus der C-Sprachfamilie ist kein
break
nötig um einen Arm zu terminieren! (Das Gegenteilige Verhalten kann
jedoch mit fallthrough
erreicht werden; siehe weiter unten.)
default
Beim if
/else
-Konstrukt fängt der else
-Block alle Fälle ab, auf die keine
der vorhergehenden if
- oder else if
-Bedingungen gepasst hat. Beim
switch
/case
-Konstrukt gibt es hierfür den default
-Block:
switch grade {
case 6:
fmt.Println("very good")
case 5:
fmt.Println("good")
case 4:
fmt.Println("sufficient")
case 3:
fmt.Println("insufficient")
case 2:
fmt.Println("bad")
case 1:
fmt.Println("very bad")
default:
fmt.Println("unknown grade")
}
fallthrough
Soll nach zutreffender Bedingung nicht nur der aktuelle Block ausgeführt werden,
sondern auch der nächste Block, kann dies mit fallthrough
(Spec) erreicht werden:
switch grade {
case 6:
fallthrough
case 5:
fallthrough
case 4:
fmt.Println("passed the exam")
case 3:
fallthrough
case 2:
fallthrough
case 1:
fmt.Println("failed the exam")
default:
fmt.Println("unknown result")
}
Für die Werte 4
, 5
und 6
von grade
gilt die Prüfung als bestanden, für
die Werte 1
, 2
und 3
als nicht bestanden.
Schleifen (Loops)
Go kennt nur eine einzige Art der Schleife: for
(Spec). Diese kann aber auf
verschiedene Arten verwendet werden, um vergleichbare Konstrukte in anderen
Sprachen (while
, foreach
) emulieren zu können.
for
Die for
-Schleife folgt der Syntax:
for initializer; condition; end statement {
repeated block
}
Beispielsweise:
for i := 0; i < 10; i++ {
fmt.Printf("%d ", i)
}
fmt.Println()
Ausgabe:
0 1 2 3 4 5 6 7 8 9
Eine entsprechende while
-Schleife liesse sich folgendermassen formulieren,
wobei initializer
und end statement
entsprechend vor die Schleife oder in
den Schleifenblock verschoben werden:
j := 0
for j < 10 {
fmt.Printf("%d ", j)
j++
}
fmt.Println()
continue
Soll der Schleifenblock unterbrochen und die Ausführung bei der nächsten
Iteration fortgesetzt werden, kann dies mit continue
(Spec) erreicht werden:
for x := 0; x < 10; x++ {
if x%2 == 0 {
continue
}
fmt.Printf("%d ", x)
}
fmt.Println()
Ausgabe (nur ungerade Zahlen):
1 3 5 7 9
break
Soll die Schleife (vorzeitig) beendet werden, kann hierzu break
(Spec) verwendet werden:
sum, maxSum := 0, 15
for y := 0; y < 10; y++ {
sum += y
fmt.Printf("%d", y)
if sum >= maxSum {
break
} else {
fmt.Print(" + ")
}
}
fmt.Println(" >=", maxSum)
Ausgabe:
0 + 1 + 2 + 3 + 4 + 5 >= 15
range
Schleifen werden oftmals verwendet um über Slices zu iterieren:
fibs := []int{1, 1, 2, 3, 5, 8}
for i := 0; i < len(fibs); i++ {
value := fibs[i]
fmt.Printf("%d: %d\n", i, value)
}
Hier wird die Indexvariable i
verwendet, um die Indizes von 0 bis zur Länge
(exklusiv) des Slices fibs
hochzuzählen. Mithilfe der Indexvariable wird der
Wert ermittelt; anschliessend werden Index und Wert zusammen ausgegeben:
0: 1
1: 1
2: 2
3: 3
4: 5
5: 8
Solche Konstrukte werden sehr häufig gebraucht und können darum mit range
abgekürzt werden:
for i, value := range fibs {
fmt.Printf("%d: %d\n", i, value)
}
Mit range
werden Index und Wert paarweise zurückgegeben. Man kann aber den
Wert auch ignorieren und nur den Index (i
) zuweisen:
for i := range fibs {
value := fibs[i]
fmt.Printf("%d: %d\n", i, value)
}
Ist der Index nicht von Interesse, kann man ihm der Pseudo-Variablen _
zuweisen, um ihn zu ignorieren:
for _, value := range fibs {
fmt.Printf("%d ", value)
}
fmt.Println()
Ausgabe:
1 1 2 3 5 8
Bei einer map
, wo die Indizes beliebig sind, erfolgt die Iteration ebenfalls
mittels range
:
zipCodes := map[int]string{
1200: "Geneva",
3000: "Bern",
6000: "Lucerne",
7000: "Chur",
8000: "Zurich",
}
for zipCode, town := range zipCodes {
fmt.Printf("%d %s\n", zipCode, town)
}
Ausgabe:
8000 Zurich
1200 Geneva
3000 Bern
6000 Lucerne
7000 Chur
Wert und Schlüssel können gleichermassen (zipCode := range zipCodes
bzw. _, town := range zipCodes
) ignoriert werden, wenn sie nicht von Interesse sind.