Add German translation of chapter 2

This commit is contained in:
digitalcraftsman
2015-08-07 22:53:30 +02:00
parent c60c38103e
commit 4da3f25821
9 changed files with 1429 additions and 1412 deletions

View File

@@ -1,516 +1,523 @@
# 2.3 Control statements and functions
# 2.3 Kontrollstrukturen und Funktionen
In this section, we are going to talk about control statements and function operations in Go.
In diesem Kapite
werfen wir einen Blick auf Kontrollstrukturen und Funktionen in Go.
## Control statement
## Kontrollstrukturen
The greatest invention in programming is flow control. Because of them, you are able to use simple control statements that can be used to represent complex logic. There are three categories of flow control: conditional, cycle control and unconditional jump.
Die beste Erfindung in der Programmierung ist die Möglichkeit, den Ablauf einen Programms dynamisch verändern zu können. Um dies zu ermöglichen, kannst Du Kontrollstrukturen verwenden, um komplexe, logische Verknüpfungen darzustellen. Es gibt drei Arten, den Programmablauf zu ändern: Bedingungen, Schleifen, die eine Aufgabe mehrmals durchführen können und Sprungmarken.
### if
`if` will most likely be the most common keyword in your programs. If it meets the conditions, then it does something and it does something else if not.
`if` ist das am weitverbreiteste Schlüsselwort in Deinen Programmen. Sollte eine Bedingung erfüllt sein, wird das Programm eine bestimmte Aufgabe ausführen. Wird die Bedingung nicht erfüllt, dann passiert etwas Anderes.
`if` doesn't need parentheses in Go.
In Go braucht man keine runden Klammern für eine `if`-Bedingung nutzen.
if x > 10 {
fmt.Println("x is greater than 10")
} else {
fmt.Println("x is less than or equal to 10")
}
The most useful thing concerning `if` in Go is that it can have one initialization statement before the conditional statement. The scope of the variables defined in this initialization statement are only available inside the block of the defining `if`.
if x > 10 {
fmt.Println("x ist größer als 10")
} else {
fmt.Println("x ist kleiner gleich 10")
}
// initialize x, then check if x greater than
if x := computedValue(); x > 10 {
fmt.Println("x is greater than 10")
} else {
fmt.Println("x is less than 10")
}
Der nützlichste Aspekt in Bezug auf `if` ist, dass man vor der Überprüfung der Bedingung noch Startwerte vergeben kann. Die Variablen mit den Startwerten sind aber nur im `if`-Block selbst abrufbar.
// the following code will not compile
fmt.Println(x)
Use if-else for multiple conditions.
// Gebe x einen Startwert und überprüfe dann, ob x größer als 10 ist
if x := berechneEinenWert(); x > 10 {
fmt.Println("x ist größer als 10")
} else {
fmt.Println("x ist kleiner als 10")
}
// Der folgende Code kann nicht kompiliert werden
fmt.Println(x)
Benutze if-else für mehrere Bedingungen.
if integer == 3 {
fmt.Println("Der Integer ist gleich 3")
} else if integer < 3 {
fmt.Println("Der Integer ist kleiner als 3")
} else {
fmt.Println("Der Integer ist größer als 3")
}
if integer == 3 {
fmt.Println("The integer is equal to 3")
} else if integer < 3 {
fmt.Println("The integer is less than 3")
} else {
fmt.Println("The integer is greater than 3")
}
### goto
Go has a `goto` keyword, but be careful when you use it. `goto` reroutes the control flow to a previously defined `label` within the body of same code block.
func myFunc() {
i := 0
Here: // label ends with ":"
fmt.Println(i)
i++
goto Here // jump to label "Here"
}
The label name is case sensitive.
Go umfasst auch das `goto` Schlüsselwort, aber benutze es weise. `goto` verändert den Ablauf des Programms, indem es an einer vorher definierten Sprungmarke im selben Codeabschnitt weiterläuft.
func meineFunktion() {
i := 0
Hier: // Sprungmarken enden mit einem ":"
fmt.Println(i)
i++
goto Hier // Springe nun zur Sprungmarke "Hier"
}
Es wird zwischen der Groß- und Kleinschreibung von Sprungmarken unterschieden. `Hier` ist nicht das Selbe wie `hier`.
### for
`for` is the most powerful control logic in Go. It can read data in loops and iterative operations, just like `while`.
`for` ist die mächtigste Kontrollstruktur in Go. Sie kann Daten lesen und sie in jedem Durchgang verändern, ähnlich wie `while`.
for expression1; expression2; expression3 {
//...
}
`expression1`, `expression2` and `expression3` are all expressions, where `expression1` and `expression3` are variable definitions or return values from functions, and `expression2` is a conditional statement. `expression1` will be executed once before looping, and `expression3` will be executed after each loop.
for Ausdruck1; Ausdruck2; Ausdruck3 {
//...
}
Examples are more useful than words.
`Ausdruck1`, `Ausdruck2` und `Ausdruck3` sind alle Ausdrücke, aber bei `Ausdruck1` und `Ausdruck3` handelt es sich aber entweder um eine Deklaration von Variablen oder um Rückgabewerte von Funktionen. `Ausdruck2` ist dagegen eine zu erfüllende Bedingung . `Ausdruck1` wird nur einmal vor der Ausführung der Schleife verändert, aber `Ausdruck3` kann mit jedem Durchlauf verändert werden.
package main
import "fmt"
Aber Beispiele sagen mehr als tausend Worte.
func main(){
sum := 0;
for index:=0; index < 10 ; index++ {
sum += index
}
fmt.Println("sum is equal to ", sum)
}
// Printsum is equal to 45
package main
import "fmt"
Sometimes we need multiple assignments, but Go doesn't have the `,` operator, so we use parallel assignment like `i, j = i + 1, j - 1`.
func main(){
summe := 0;
for index:=0; index < 10 ; index++ {
summe += index
}
fmt.Println("summe hat den Wert ", summe)
}
// Ausgabe: summe hat den Wert 45
We can omit `expression1` and `expression3` if they are not necessary.
Manchmal müssen wir am Anfang mehrere Deklarationen vornehmen. Da Go für die Trennung der Variablen über kein `,` verfügt, können die Variablen nicht einzeln deklariert werden. Ein Ausweg bietet die parallele Deklaration: `i, j = i + 1, j - 1`.
sum := 1
for ; sum < 1000; {
sum += sum
}
Omit `;` as well. Feel familiar? Yes, it's identical to `while`.
Wenn nötig, können wir `Ausdruck1` und `Ausdruck3` auch einfach auslassen.
sum := 1
for sum < 1000 {
sum += sum
}
There are two important operations in loops which are `break` and `continue`. `break` jumps out of the loop, and `continue` skips the current loop and starts the next one. If you have nested loops, use `break` along with labels.
summe := 1
for ; summe < 1000; {
summe += summe
}
for index := 10; index>0; index-- {
if index == 5{
break // or continue
}
fmt.Println(index)
}
// break prints 10、9、8、7、6
// continue prints 10、9、8、7、6、4、3、2、1
`for` can read data from `slice` and `map` when it is used together with `range`.
Auch das `;` kann ausgelassen werden. Kommt es Dir bekannt vor? Denn ist das gleiche Verhalten wie bei `while`.
for k,v:=range map {
fmt.Println("map's key:",k)
fmt.Println("map's val:",v)
}
Because Go supports multi-value returns and gives compile errors when you don't use values that were defined, you may want to use `_` to discard certain return values.
summe := 1
for summe < 1000 {
summe += summe
}
Es gibt mit `break` und `continue` zwei wichtige Schlüsselwörter bei der Verwendung von Schleifen. `break` wird zum "Ausbrechen" aus einer Schleife genutzt, während `continue` zum nächsten Durchlauf springt. Sollten verschachtelte Schleifen genutzt werden, dann kombiniere `break` mit einer Sprungmarke.
for index := 10; index>0; index-- {
if index == 5{
break // oder continue
}
fmt.Println(index)
}
// break gibt 10、9、8、7、6 aus
// continue gibt 10、9、8、7、6、4、3、2、1 aus
`for` kann auch die Elemente aus einem `slice` oder einer `map` lesen, wenn dies im Zusammenspiel mit `range` geschieht.
for k, v := range map {
fmt.Println("map's Schlüssel:",k)
fmt.Println("map's Wert:",v)
}
Go unterstützt die Rückgabe mehrerer Werte und gibt eine Fehlermeldung aus, wenn nicht genauso viele Variablen zum Speichern vorhanden sind. Solltest Du einen Wert nicht brauchen, kannst Du ihn mit `_` verwerfen.
for _, v := range map{
fmt.Println("map's Wert:", v)
}
for _, v := range map{
fmt.Println("map's val:", v)
}
### switch
Sometimes you may find that you are using too many `if-else` statements to implement some logic, which may make it difficult to read and maintain in the future. This is the perfect time to use the `switch` statement to solve this problem.
Manchmal findest Du Dich in einer Situation vor, indem viele Bedingungen mit `if-else` geprüft werden müssen. Der Code kann dann schnell unleserlich und schwer anpassbar werden. Alternativ kannst Du Bedingungen auch mit `switch` prüfen.
switch sExpr {
case expr1:
some instructions
case expr2:
some other instructions
case expr3:
some other instructions
default:
other code
}
The type of `sExpr`, `expr1`, `expr2`, and `expr3` must be the same. `switch` is very flexible. Conditions don't have to be constants and it executes from top to bottom until it matches conditions. If there is no statement after the keyword `switch`, then it matches `true`.
switch Audruck {
case Fall1:
Eine Aufgabe
case Fall3:
Eine andere Aufgabe
case Fall3:
Eine andere Aufgabe
default:
Anderer Code
}
i := 10
switch i {
case 1:
fmt.Println("i is equal to 1")
case 2, 3, 4:
fmt.Println("i is equal to 2, 3 or 4")
case 10:
fmt.Println("i is equal to 10")
default:
fmt.Println("All I know is that i is an integer")
}
In the fifth line, we put many values in one `case`, and we don't need to add the `break` keyword at the end of `case`'s body. It will jump out of the switch body once it matched any case. If you want to continue to matching more cases, you need to use the`fallthrough` statement.
Der Datentyp von `Fall1`, `Fall2` und `Fall3` muss der selbe sein. `switch` kann sehr flexible eingesetzt werden. Bedingungen müssen keine Konstanten sein und die einzelnen Fälle werden von oben nach unten geprüft, bis eine passender gefunden wurde. Sollte `switch` kein Ausdruck folgen, dann dann wird von einem `bool` ausgegangen und es wird der Code im Fall `true` ausgeführt.
integer := 6
switch integer {
case 4:
fmt.Println("integer <= 4")
fallthrough
case 5:
fmt.Println("integer <= 5")
fallthrough
case 6:
fmt.Println("integer <= 6")
fallthrough
case 7:
fmt.Println("integer <= 7")
fallthrough
case 8:
fmt.Println("integer <= 8")
fallthrough
default:
fmt.Println("default case")
}
This program prints the following information.
i := 10
switch i {
case 1:
fmt.Println("i ist gleich 1")
case 2, 3, 4:
fmt.Println("i ist gleich 2, 3 oder 4")
case 10:
fmt.Println("i ist gleich 10")
default:
fmt.Println("ich weiß nur, dass i ein Integer ist")
}
integer <= 6
integer <= 7
integer <= 8
default case
In der fünften Zeile nutzen wir gleich mehrere Fälle auf einmal. Außerdem brauchen wir nicht `break` am Ende jedes Falls einfügen, wie es in anderen Programmiersprachen üblich ist. Mit dem Schlüsselwort `fallthrough` kannst Du nach einem Treffer auch alle folgenden Fälle ausführen.
## Functions
integer := 6
switch integer {
case 4:
fmt.Println("integer <= 4")
fallthrough
case 5:
fmt.Println("integer <= 5")
fallthrough
case 6:
fmt.Println("integer <= 6")
fallthrough
case 7:
fmt.Println("integer <= 7")
fallthrough
case 8:
fmt.Println("integer <= 8")
fallthrough
default:
fmt.Println("Standardfall")
}
Use the `func` keyword to define a function.
Das Programm gibt folgende Informationen aus.
func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
// function body
// multi-value return
return value1, value2
}
We can extrapolate the following information from the example above.
integer <= 6
integer <= 7
integer <= 8
Standardfall
- Use keyword `func` to define a function `funcName`.
- Functions have zero, one or more than one arguments. The argument type comes after the argument name and arguments are separated by `,`.
- Functions can return multiple values.
- There are two return values named `output1` and `output2`, you can omit their names and use their type only.
- If there is only one return value and you omitted the name, you don't need brackets for the return values.
- If the function doesn't have return values, you can omit the return parameters altogether.
- If the function has return values, you have to use the `return` statement somewhere in the body of the function.
## Funktionen
Let's see one practical example. (calculate maximum value)
Nutze das Schlüsselwort `func`, um eine Funktion einzuleiten.
package main
import "fmt"
func funcName(eingabe1 typ1, eingabe2 typ2) (ausgabe1 typ1, ausgabe2 typ2) {
// Körper bzw. Rumpf der Funktion
// Mehrere Werte werden zurückgegeben
return wert1, wert2
}
// return greater value between a and b
func max(a, b int) int {
if a > b {
return a
}
return b
}
Dem gezeigten Beispiel können wir somit folgende Informationen entnehmen:
func main() {
x := 3
y := 4
z := 5
- Nutze das Schlüsselwort `func`, um die Funktion `funcName` zu erstellen.
- Funktionen können ein, mehrere oder kein Argument übergeben werden. Der Datentyp wird nach dem Argument angegeben und Argumente werden durch ein `,` getrennt.
- Funktionen können mehrere Werte zurückgeben.
- Es gibt zwei Rückgabewerte mit den Namen `ausgabe1` and `ausgabe2`. Du kannst Ihren Namen auch einfach auslassen und nur den Datentyp im Kopf angeben.
- Wenn es nur einen Rückgabewert gibt, kannst Du die Klammer um den Datentyp im Kopf weglassen.
- Wenn keine Daten zurückgegeben werden, kannst Du `return` einfach weglassen.
- Um Werte im Körper der Funktion zurückzugeben, musst Du `return` verwenden.
max_xy := max(x, y) // call function max(x, y)
max_xz := max(x, z) // call function max(x, z)
Gucken wir uns mal ein Beispiel an, in dem wir den Größeren von zwei Werten bestimmen.
fmt.Printf("max(%d, %d) = %d\n", x, y, max_xy)
fmt.Printf("max(%d, %d) = %d\n", x, z, max_xz)
fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // call function here
}
package main
import "fmt"
In the above example, there are two arguments in the function `max`, their types are both `int` so the first type can be omitted. For instance, `a, b int` instead of `a int, b int`. The same rules apply for additional arguments. Notice here that `max` only has one return value, so we only need to write the type of its return value -this is the short form of writing it.
// Gib den größeren Wert von a und b zurück
func max(a, b int) int {
if a > b {
return a
}
return b
}
### Multi-value return
func main() {
x := 3
y := 4
z := 5
One thing that Go is better at than C is that it supports multi-value returns.
max_xy := max(x, y) // ruft die Funktion max(x, y) auf
max_xz := max(x, z) // ruft die Funktion max(x, z) auf
We'll use the following example here.
fmt.Printf("max(%d, %d) = %d\n", x, y, max_xy)
fmt.Printf("max(%d, %d) = %d\n", x, z, max_xz)
fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // rufe die Funktion hier auf
}
package main
import "fmt"
Im oberen Beispiel übergeben wir jeweils zwei Argumente vom Typ `int` an die Funktion `max`. Da beide Argumente denn gleichen Datentyp besitzen, reicht es, wenn wir diesen nur einmal angeben. So kannst Du statt `a int, b int` einfach `a, b int` schreiben. Dies gilt auch für weitere Argumente. Wie Du sehen kannst, wird nur ein Wert von `max` zurückgegeben, sodass wir den Namen der Variable weglassen können. Dies wäre einfach die Kurzschreibweise.
// return results of A + B and A * B
func SumAndProduct(A, B int) (int, int) {
return A+B, A*B
}
### Mehrere Rückgabewerte bei Funktionen
func main() {
x := 3
y := 4
Die Möglichkeit, mehrere Werte zurückgeben zu können, ist ein Aspekt, in der Go der Programmiersprache C überlegen ist.
xPLUSy, xTIMESy := SumAndProduct(x, y)
Schauen wir uns das folgende Beispiel an.
fmt.Printf("%d + %d = %d\n", x, y, xPLUSy)
fmt.Printf("%d * %d = %d\n", x, y, xTIMESy)
}
The above example returns two values without names -you have the option of naming them also. If we named the return values, we would just need to use `return` to return the values since they are initialized in the function automatically. Notice that if your functions are going to be used outside of the package, which means your function names start with a capital letter, you'd better write complete statements for `return`; it makes your code more readable.
package main
import "fmt"
func SumAndProduct(A, B int) (add int, Multiplied int) {
add = A+B
Multiplied = A*B
return
}
// Gebe die Ergebnisse von A + B und A * B zurück
func SummeUndProdukt(A, B int) (int, int) {
return A+B, A*B
}
### Variable arguments
func main() {
x := 3
y := 4
Go supports variable arguments, which means you can give an uncertain numbers of argument to functions.
xPLUSy, xMALy := SummeUndProdukt(x, y)
func myfunc(arg ...int) {}
fmt.Printf("%d + %d = %d\n", x, y, xPLUSy)
fmt.Printf("%d * %d = %d\n", x, y, xMALy)
}
`arg …int` tells Go that this is a function that has variable arguments. Notice that these arguments are type `int`. In the body of function, the `arg` becomes a `slice` of `int`.
Das obere Beispiele gibt zwei namenlose Werte zurück. Aber Du kannst sie natürlich auch bennenen, wenn Du magst. Würden wir dies tun, müssten wir mit `return` nur dessen Namen zurückgeben, da die Variablen in der Funktion bereits automatisch initialisiert wurden. Bedenke, dass Funktionen mit einen großen Buchstaben anfangen müssen, wenn Du sie außerhalb eines Pakets verwenden möchtest. Es wäre auch zu empfehlen, die Rückgabewerte zu bennenen, da dies den Code verständlicher macht.
for _, n := range arg {
fmt.Printf("And the number is: %d\n", n)
}
### Pass by value and pointers
func SummeUndProdukt(A, B int) (addiert int, multipliziert int) {
addiert = A+B
multipliziert = A*B
return
}
When we pass an argument to the function that was called, that function actually gets the copy of our variables so any change will not affect to the original variable.
### Variablen als Argumente
Let's see one example in order to prove what i'm saying.
Go unterstützt auch Variablen als Argumente, was bedeutet, das einer Funktionen auch eine unbekannte Anzahl an Argumenten übergeben werden kann.
package main
import "fmt"
func myfunc(arg ...int) {}
// simple function to add 1 to a
func add1(a int) int {
a = a+1 // we change value of a
return a // return new value of a
}
`arg …int` besagt, dass diese Funktionen beliebig viele Argumente entgegennimmt. Beachte, dass alle Argumente vom Typ `int` sind. Im Funktionskörper wird `arg` zu einem `slice` vom Typ `int`.
func main() {
x := 3
for _, n := range arg {
fmt.Printf("Und die Nummer lautet: %d\n", n)
}
### Werte und Zeiger übergeben
fmt.Println("x = ", x) // should print "x = 3"
Wenn wir ein Argument einer aufgerufenen Funktion übergeben, dann bekommt diese meist eine Kopie des Arguments und kann somit das Original nicht beeinflussen.
x1 := add1(x) // call add1(x)
Lass mich Dir ein Beispiel zeigen, um es zu beweisen.
fmt.Println("x+1 = ", x1) // should print "x+1 = 4"
fmt.Println("x = ", x) // should print "x = 3"
}
Can you see that? Even though we called `add1` with `x`, the origin value of `x` doesn't change.
package main
import "fmt"
The reason is very simple: when we called `add1`, we gave a copy of `x` to it, not the `x` itself.
// Simple Funktion, die a um 1 erhöht
func add1(a int) int {
a = a+1 // Wir verändern den Wert von a
return a // Wir geben den Wert von a zurück
}
Now you may ask how I can pass the real `x` to the function.
func main() {
x := 3
We need use pointers here. We know variables are stored in memory and they have some memory addresses. So, if we want to change the value of a variable, we must change its memory address. Therefore the function `add1` has to know the memory address of `x` in order to change its value. Here we pass `&x` to the function, and change the argument's type to the pointer type `*int`. Be aware that we pass a copy of the pointer, not copy of value.
fmt.Println("x = ", x) // Sollte "x = 3" ausgeben
package main
import "fmt"
x1 := add1(x) // Rufe add1(x) auf
// simple function to add 1 to a
func add1(a *int) int {
*a = *a+1 // we changed value of a
return *a // return new value of a
}
fmt.Println("x+1 = ", x1) // Sollte "x+1 = 4" ausgeben
fmt.Println("x = ", x) // Sollte "x = 3" ausgeben
}
func main() {
x := 3
Siehst Du es? Selbst wenn wir der Funktion `add1` die Variable `x` als Argument übergeben, um sie um 1 zu erhöhen, so blieb `x` selbst unverändert
fmt.Println("x = ", x) // should print "x = 3"
Der Grund dafür ist naheliegend: wenn wir `add1` aufrufen, übergeben wir ihr eine Kopie von `x`, und nicht `x` selbst.
x1 := add1(&x) // call add1(&x) pass memory address of x
Nun fragst Du dich bestimmt, wie ich das echte `x` einer Funktion übergeben kann.
fmt.Println("x+1 = ", x1) // should print "x+1 = 4"
fmt.Println("x = ", x) // should print "x = 4"
}
Now we can change the value of `x` in the functions. Why do we use pointers? What are the advantages?
Dafür müssen wir Zeiger verwenden. Wir wissen, das Variablen im Arbeitspeicher hinterlegt sind und sie eine Adresse für den Speicherort besitzen. Um einen Wert zu ändern, müssen wir auch die Adresse des Speicherorts wissen. Daher muss der Funktion `add1` diese Adresse von `x` bekannt sein, um diese ändern zu können. Hier übergeben wir `&x` der Funktion als Argument und ändern damit den Datentyp des Arguments in `*int`. Beachte, dass wir eine Kopie des Zeigers übergeben, und nicht des Wertes.
- Allows us to use more functions to operate on one variable.
- Low cost by passing memory addresses (8 bytes), copy is not an efficient way, both in terms of time and space, to pass variables.
- `string`, `slice`, `map` are reference types, so they use pointers when passing to functions by default. (Attention: If you need to change the length of `slice`, you have to pass pointers explicitly)
package main
import "fmt"
// Simple Funktion, um a um 1 zu erhöhen
func add1(a *int) int {
*a = *a+1 // Wir verändern den Wert von a
return *a // Wir geben den Wert von a zurück
}
func main() {
x := 3
fmt.Println("x = ", x) // Sollte "x = 3" ausgeben
x1 := add1(&x) // Rufe add1(&x) auf und übergebe die Adresse des Speicherortes von x
fmt.Println("x+1 = ", x1) // Sollte "x+1 = 4" ausgeben
fmt.Println("x = ", x) // Sollte "x = 4" ausgeben
}
Nun können wir den Wert von `x` in der Funktion ändern. Aber warum nutzen wir Zeiger? Was sind die Vorteile?
- Es erlaubt uns, mit mehreren Funktionen eine Variable direkt zu benutzen und zu verändern.
- Es braucht wenig Ressourcen, um die Speicheradresse (8 Bytes) zu übergeben. Kopien sind weniger effizient im Kontext von Zeit und Speichergröße.
- `string`, `slice` und `map` sind Referenztypen, die automatisch die Referenz (bzw. den Zeiger) der Funktion übergeben. (Achtung: Wenn Du die Länge eines `slice` ändern möchtest, musst Du den Zeiger explizit übergeben).
### defer
Go has a well designed keyword called `defer`. You can have many `defer` statements in one function; they will execute in reverse order when the program executes to the end of functions. In the case where the program opens some resource files, these files would have to be closed before the function can return with errors. Let's see some examples.
Go besitzt mit `defer` ein weiteres nützliches Schlüsselwort. Du kannst `defer` mehrmals in einer Funktion nutzen. Sie werden in umgekehrter Reihenfolge am Ende einer Funktion ausgeführt. Im dem Fall, dass Dein Programm eine Datei öffnet, muss diese erst wieder geschlossen werden, bevor Fehler zurückgeben werden können. Schauen wir uns ein paar Beispiele an.
func ReadWrite() bool {
file.Open("file")
// Do some work
if failureX {
file.Close()
return false
}
if failureY {
file.Close()
return false
func LesenSchreiben() bool {
file.Open("Datei")
// Mache etwas
if FehlerX {
file.Close()
return false
}
file.Close()
return true
}
We saw some code being repeated several times. `defer` solves this problem very well. It doesn't only help you to write clean code but also makes your code more readable.
if FehlerY {
file.Close()
return false
}
func ReadWrite() bool {
file.Open("file")
defer file.Close()
if failureX {
return false
}
if failureY {
return false
}
return true
}
If there are more than one `defer`s, they will execute by reverse order. The following example will print `4 3 2 1 0`.
file.Close()
return true
}
Wir haben gesehen, dass hier der selbe Code öfters wiederhohlt wird. `defer` löst dieses Problem ziemlich elegant. Es hilft Dir nicht nur sauberen Code zu schreiben, sodern fördert auch noch dessen Lesbarkeit.
for i := 0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
### Functions as values and types
func LesenSchreiben() bool {
file.Open("Datei")
defer file.Close()
if FehlerX {
return false
}
if FehlerY {
return false
}
return true
}
Wird `defer` mehr als einmal genutzt, werden sie in umgekehrter Reihenfolge ausgeführt. Das folgende Beispiel wird `4 3 2 1 0` ausgeben.
Functions are also variables in Go, we can use `type` to define them. Functions that have the same signature can be seen as the same type.
for i := 0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
### Funktionen als Werte und Datentypen
type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...])
What's the advantage of this feature? The answer is that it allows us to pass functions as values.
Funktionen sind auch Variablen in Go, die wir mit `type` definieren können. Funktionen mit der selben Signatur, also dem gleichen Aufbau, können als der selbe Datentyp betrachtet werden.
package main
import "fmt"
type schreibeName func(eingabe1 eingabeTyp1 , eingabe2 eingabeTyp2 [, ...]) (ergebnis1 ergebnisTyp1 [, ...])
type testInt func(int) bool // define a function type of variable
Was ist der Vorteil dieser Fähigkeit? Ganz einfach: wir können somit Funktionen als Werte übergeben.
func isOdd(integer int) bool {
if integer%2 == 0 {
return false
}
return true
}
package main
import "fmt"
func isEven(integer int) bool {
if integer%2 == 0 {
return true
}
return false
}
type testeInt func(int) bool // Definiere eine Funktion als Datentyp
// pass the function `f` as an argument to another function
func istUngerade(integer int) bool {
if integer%2 == 0 {
return false
}
return true
}
func filter(slice []int, f testInt) []int {
var result []int
for _, value := range slice {
if f(value) {
result = append(result, value)
}
}
return result
}
func istGerade(integer int) bool {
if integer%2 == 0 {
return true
}
return false
}
func main(){
slice := []int {1, 2, 3, 4, 5, 7}
fmt.Println("slice = ", slice)
odd := filter(slice, isOdd) // use function as values
fmt.Println("Odd elements of slice are: ", odd)
even := filter(slice, isEven)
fmt.Println("Even elements of slice are: ", even)
}
It's very useful when we use interfaces. As you can see `testInt` is a variable that has function type, and return values and arguments of `filter` are the same as `testInt`. Therefore, we can have complex logic in our programs, while maintaining flexibility in our code.
// Übergebe die Funktion f als Argument an eine Andere Funktion
### Panic and Recover
func filter(slice []int, f testeInt) []int {
var ergebnis []int
for _, wert := range slice {
if f(wert) {
ergebnis = append(ergebnis, wert)
}
}
return ergebnis
}
Go doesn't have `try-catch` structure like Java does. Instead of throwing exceptions, Go uses `panic` and `recover` to deal with errors. However, you shouldn't use `panic` very much, although it's powerful.
func main(){
slice := []int {1, 2, 3, 4, 5, 7}
fmt.Println("slice = ", slice)
ungerade := filter(slice, istUngerade) // Nutze die Funktion als Wert
fmt.Println("Die ungeraden Elemente von slice lauten: ", ungerade)
gerade := filter(slice, istGerade)
fmt.Println("Die geraden Elemente von slice lauten: ", gerade)
}
Panic is a built-in function to break the normal flow of programs and get into panic status. When a function `F` calls `panic`, `F` will not continue executing but its `defer` functions will continue to execute. Then `F` goes back to the break point which caused the panic status. The program will not terminate until all of these functions return with panic to the first level of that `goroutine`. `panic` can be produced by calling `panic` in the program, and some errors also cause `panic` like array access out of bounds errors.
Dies ist sehr nützlich, wenn wir Interfaces nutzen. Wie Du sehen kannst, wird `testeInt` eine Funktion als Argument übergeben, und die Rückgabewerte besitzen genau den gleichen Datentyp. Somit können wir komplexe Zusammenhänge in unseren Programen flexible warten und erweitern.
Recover is a built-in function to recover `goroutine`s from panic status. Calling `recover` in `defer` functions is useful because normal functions will not be executed when the program is in the panic status. It catches `panic` values if the program is in the panic status, and it gets `nil` if the program is not in panic status.
### Panic und Recover
The following example shows how to use `panic`.
Go besitzt keine `try-catch`-Struktur, wie es in Java der Fall ist. Statt eine Exception zu erstellen, nutzt Go `panic` und `recover`. Auch wenn `panic` sehr mächtig ist, solltest Du es nicht all zu oft benutzen.
var user = os.Getenv("USER")
`panic` ist eine eingebaute Funktion, um den normalen Ablauf des Programms zu unterbrechen und es in eine Art Panikmodus zu versetzen. Wenn die Funktion `F` `panic` aufruft, wird in ihr außer `defer` nichts weiter ausgeführt. Danach geht das Programm zum Ursprungsort des Fehlers zurück. Dies wird nicht geschlossen, ehe alle Funktionen einer `goroutine` `panic` zurückgeben. Einige Fehler erzeugen auch selbst einen Aufruf von `panic`, z.B. wenn der reservierte Speicherplatz eines Arrays nicht ausreicht.
func init() {
if user == "" {
panic("no value for $USER")
}
}
The following example shows how to check `panic`.
`recover` ist ebenfalls eine eingebaute Fuktion, um `goroutinen` vom Panikmodus zu erlösen. Das Aufrufen von `recover` im Zusammenspiel mit `defer` ist sehr nützlich, da normale Funktionen nicht mehr ausgeführt werden, wenn sich das Programm erst einmal im Panikmodus befindet. Die Werte von `panic` werden so aufgefangen, wenn sich das Programm im Panikmodus befindet. Ansonsten wird `nil` zurückgegeben.
func throwsPanic(f func()) (b bool) {
defer func() {
if x := recover(); x != nil {
b = true
}
}()
f() // if f causes panic, it will recover
return
}
### `main` function and `init` function
Das folgende Beispiel soll die Nutzung von `panic` verdeutlichen.
Go has two retentions which are called `main` and `init`, where `init` can be used in all packages and `main` can only be used in the `main` package. These two functions are not able to have arguments or return values. Even though we can write many `init` functions in one package, I strongly recommend writing only one `init` function for each package.
var benutzer = os.Getenv("BENUTZER")
Go programs will call `init()` and `main()` automatically, so you don't need to call them by yourself. For every package, the `init` function is optional, but `package main` has one and only one `main` function.
func init() {
if benutzer == "" {
panic("Kein Wert für $BENUTZER gefunden")
}
}
Das nächste Beispiel zeigt, wie der Status von `panic` aufgerufen werden kann.
Programs initialize and begin execution from the `main` package. If the `main` package imports other packages, they will be imported in the compile time. If one package is imported many times, it will be only compiled once. After importing packages, programs will initialize the constants and variables within the imported packages, then execute the `init` function if it exists, and so on. After all the other packages are initialized, programs will initialize constants and variables in the `main` package, then execute the `init` function inside the package if it exists. The following figure shows the process.
func erzeugePanic(f func()) (b bool) {
defer func() {
if x := recover(); x != nil {
b = true
}
}()
f() // Wenn 'f' 'panic' verursacht, wird versucht, dies mit 'recover' zu beheben
return
}
### Die `main` und `init` Funktion
Go hat zwei besondere Funktionen mit dem Namen `main` und `init`, wobei `init` in jedem Paket aufgerufen werden kann und dies bei `main` nur im gleichnahmigen Paket möglich ist. Beide Funktionen besitzen weder Argumente, die übergeben werden können, noch geben sie Werte zurück. Es ist möglich, die `init`-Funktion innerhalb eines Pakets mehrmals aufzurufen, aber ich empfehle jedoch die Nutzung einer einzelnen, da es sonst unübersichtlich werden kann.
Go ruft `init()` und `main()` automatisch auf, sodass dies nicht manuell geschehen muss. Für jedes Paket ist die `init`-Funktion optional, jedoch ist die `main`-Funktion unter `package main` obligatorisch und kann lediglich ein einziges Mal genutzt werden.
Programme werden vom `main`-Paket aus gestarten. Wenn dieses Paket noch weitere Pakete importiert, geschieht dies einzig während der Kompilierung. Nach dem Importieren von Paketen werden zu erst dessen Konstanten und Variablen initialisiert, dann wird `init` aufgerufen usw. Nachdem alle Pakete erfolgreich initialisiert wurden, geschieht all dies im `main`-Paket. Die folgende Grafik illustriert diesen Ablauf.
![](images/2.3.init.png?raw=true)
Figure 2.6 Flow of programs initialization in Go
Abbildung 2.6 Der Initialisierungsablauf eines Programms in Go
### import
We use `import` very often in Go programs as follows.
Oftmals verwenden wir `import` in unseren Go-Programmen wie folgt.
import(
"fmt"
)
Then we use functions in that package as follows.
import(
"fmt"
)
Dann nutzen wir die Funktionen aus dem Paket wie im Beispiel:
fmt.Println("hello world")
`fmt` is from Go standard library, it is located within $GOROOT/pkg. Go supports third-party packages in two ways.
fmt.Println("Hallo Welt")
1. Relative path
import "./model" // load package in the same directory, I don't recommend this way.
2. Absolute path
import "shorturl/model" // load package in path "$GOPATH/pkg/shorturl/model"
There are some special operators when we import packages, and beginners are always confused by these operators.
`fmt` stammt aus Gos Standardbibliothek, welche sich unter $GOROOT/pkg befindet. Go unterstützt Pakete von Dritten auf zwei Wege.
1. Dot operator.
Sometime we see people use following way to import packages.
import(
. "fmt"
)
The dot operator means you can omit the package name when you call functions inside of that package. Now `fmt.Printf("Hello world")` becomes to `Printf("Hello world")`.
2. Alias operation.
It changes the name of the package that we imported when we call functions that belong to that package.
import(
f "fmt"
)
Now `fmt.Printf("Hello world")` becomes to `f.Printf("Hello world")`.
3. `_` operator.
This is the operator that is difficult to understand without someone explaining it to you.
import (
"database/sql"
_ "github.com/ziutek/mymysql/godrv"
)
The `_` operator actually means we just want to import that package and execute its `init` function, and we are not sure if want to use the functions belonging to that package.
1. Relativer Pfad
import `./model` // Importiert ein Paket aus dem selben Verzeichnis, jedoch empfehle ich diese Methode nicht.
2. Absoluter Pfad
import `shorturl/model` // Lade das Paket mit dem Pfad "$GOPATH/pkg/shorturl/model"
Es gibt spezielle Operatoren beim Importieren von Paketen, die Anfänger oftmals verwirren.
1. Punkt Operator.
Manchmal sieht man, wie Pakete auf diesem Weg importiert werden.
import(
. "fmt"
)
Der Punkt Operator bedeutet einfach, dass der Name des Paketes beim Aufruf von dessen Funktion weggelassen werden kann. So wird aus `fmt.Printf("Hallo Welt")` einfach `Printf("Hallo Welt")`.
2. Alias Operation.
Dieser verändert den Namen des Paketes, denn wir beim Aufruf von Funktionen angeben müssen.
import(
f "fmt"
)
Aus `fmt.Printf("Hallo Welt")` wird dann `f.Printf("Hallo Welt")`.
3. `_` Operator.
Dieser Operator ist ohne Erklärung ein wenig schwer zu erklären.
import (
"database/sql"
_ "github.com/ziutek/mymysql/godrv"
)
Der `_` Operator bedeutet einfach nur, das wir das Paket importieren möchten und das dessen `init`-Funktion ausgeführt werden soll. Jedoch sind wir uns nicht sicher, ob wir die anderen Funktionen dieses Pakets überhaupt nutzen wollen.
## Links
- [Directory](preface.md)
- Previous section: [Go foundation](02.2.md)
- Next section: [struct](02.4.md)
- [Inhaltsverzeichnis](preface.md)
- Vorheriger Abschnitt: [Grundlagen von Go](02.2.md)
- Nächster Abschnitt: [Struct](02.4.md)