Fixed grammar and syntax in 02.3.md
This commit is contained in:
108
en/eBook/02.3.md
108
en/eBook/02.3.md
@@ -1,14 +1,14 @@
|
||||
# 2.3 Control statements and functions
|
||||
|
||||
In this section, we are going to talk about control statements and function operation in Go.
|
||||
In this section, we are going to talk about control statements and function operations in Go.
|
||||
|
||||
## Control statement
|
||||
|
||||
The greatest inventions in programming language is flow control. Because of them, you are able to use simple control statements represent complex logic. There are three categories, conditional, cycle control and unconditional jump.
|
||||
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.
|
||||
|
||||
### if
|
||||
|
||||
`if` is the most common keyword in your programs. If it meets the conditions then does something, does something else if not.
|
||||
`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` doesn't need parentheses in Go.
|
||||
|
||||
@@ -18,7 +18,7 @@ The greatest inventions in programming language is flow control. Because of them
|
||||
fmt.Println("x is less than 10")
|
||||
}
|
||||
|
||||
The most useful thing of `if` in Go is that it can have one initialization statement before the conditional statement. The scope of variables which are defined in this initialization statement is only in the block of `if`.
|
||||
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`.
|
||||
|
||||
// initialize x, then check if x greater than
|
||||
if x := computedValue(); x > 10 {
|
||||
@@ -42,7 +42,7 @@ Use if-else for multiple conditions.
|
||||
|
||||
### goto
|
||||
|
||||
Go has `goto`, be careful when you use it. `goto` has to jump to the `label` that in the body of same code block.
|
||||
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
|
||||
@@ -52,19 +52,19 @@ Go has `goto`, be careful when you use it. `goto` has to jump to the `label` tha
|
||||
goto Here // jump to label "Here"
|
||||
}
|
||||
|
||||
Label name is case sensitive.
|
||||
The label name is case sensitive.
|
||||
|
||||
### for
|
||||
|
||||
`for` is the most powerful control logic in Go, it can read data in loops and iterative operations, just like `while`.
|
||||
`for` is the most powerful control logic in Go. It can read data in loops and iterative operations, just like `while`.
|
||||
|
||||
for expression1; expression2; expression3 {
|
||||
//...
|
||||
}
|
||||
|
||||
`expression1`, `expression2` and `expression3` are all expressions obviously, where `expression1` and `expression3` are variable defination or return values from functions, and `expression2` is a conditional statement. `expression1` will be executed before every loop, and `expression3` will be after.
|
||||
`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 before every loop, and `expression3` will be executed after.
|
||||
|
||||
An example is more useful than hundreds of words.
|
||||
Examples are more useful than words.
|
||||
|
||||
package main
|
||||
import "fmt"
|
||||
@@ -78,7 +78,7 @@ An example is more useful than hundreds of words.
|
||||
}
|
||||
// Print:sum is equal to 45
|
||||
|
||||
Sometimes we need multiple assignments, but Go doesn't have operator `,`, so we use parallel assignment like `i, j = i + 1, j - 1`.
|
||||
Sometimes we need multiple assignments, but Go doesn't have the `,` operator, so we use parallel assignment like `i, j = i + 1, j - 1`.
|
||||
|
||||
We can omit `expression1` and `expression3` if they are not necessary.
|
||||
|
||||
@@ -87,14 +87,14 @@ We can omit `expression1` and `expression3` if they are not necessary.
|
||||
sum += sum
|
||||
}
|
||||
|
||||
Omit `;` as well. Feel familiar? Yes, it's `while`.
|
||||
Omit `;` as well. Feel familiar? Yes, it's identical to `while`.
|
||||
|
||||
sum := 1
|
||||
for sum < 1000 {
|
||||
sum += sum
|
||||
}
|
||||
|
||||
There are two important operations in loops which are `break` and `continue`. `break` jumps out the loop, and `continue` skips current loop and starts next one. If you have nested loops, use `break` with labels together.
|
||||
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.
|
||||
|
||||
for index := 10; index>0; index-- {
|
||||
if index == 5{
|
||||
@@ -105,14 +105,14 @@ There are two important operations in loops which are `break` and `continue`. `b
|
||||
// break prints 10、9、8、7、6
|
||||
// continue prints 10、9、8、7、6、4、3、2、1
|
||||
|
||||
`for` could read data from `slice` and `map` when it is used with `range`.
|
||||
`for` can read data from `slice` and `map` when it is used together with `range`.
|
||||
|
||||
for k,v:=range map {
|
||||
fmt.Println("map's key:",k)
|
||||
fmt.Println("map's val:",v)
|
||||
}
|
||||
|
||||
Because Go supports multi-value return and gives compile errors when you don't use values that was defined, so you may want to use `_` to discard some return values.
|
||||
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.
|
||||
|
||||
for _, v := range map{
|
||||
fmt.Println("map's val:", v)
|
||||
@@ -120,7 +120,7 @@ Because Go supports multi-value return and gives compile errors when you don't u
|
||||
|
||||
### switch
|
||||
|
||||
Sometimes you may think you use too much `if-else` to implement some logic, also it's not looking nice and herd to maintain in the future. Now it's time to use `switch` to solve this problem.
|
||||
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 maitain in the future. This is the perfect time to use the `switch` statement to solve this problem.
|
||||
|
||||
switch sExpr {
|
||||
case expr1:
|
||||
@@ -133,7 +133,7 @@ Sometimes you may think you use too much `if-else` to implement some logic, also
|
||||
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, it executes from top to down until it matches conditions. If there is no statement after keyword `switch`, then it matches `true`.
|
||||
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`.
|
||||
|
||||
i := 10
|
||||
switch i {
|
||||
@@ -147,7 +147,7 @@ The type of `sExpr`, `expr1`, `expr2`, and `expr3` must be the same. `switch` is
|
||||
fmt.Println("All I know is that i is an integer")
|
||||
}
|
||||
|
||||
In fifth line, we put many values in one `case`, and we don't need `break` in the end of `case` body. It will jump out of switch body once it matched any case. If you want to continue to match more cases, you need to use statement `fallthrough`.
|
||||
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.
|
||||
|
||||
integer := 6
|
||||
switch integer {
|
||||
@@ -170,7 +170,7 @@ In fifth line, we put many values in one `case`, and we don't need `break` in th
|
||||
fmt.Println("default case")
|
||||
}
|
||||
|
||||
This program prints following information.
|
||||
This program prints the following information.
|
||||
|
||||
integer <= 6
|
||||
integer <= 7
|
||||
@@ -179,7 +179,7 @@ This program prints following information.
|
||||
|
||||
## Functions
|
||||
|
||||
Use the keyword `func` to define a function.
|
||||
Use the `func` keyword to define a function.
|
||||
|
||||
func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
|
||||
// function body
|
||||
@@ -187,15 +187,15 @@ Use the keyword `func` to define a function.
|
||||
return value1, value2
|
||||
}
|
||||
|
||||
We can get following information from above example.
|
||||
We extrapolate the following information from the example above.
|
||||
|
||||
- Use keyword `func` to define a function `funcName`.
|
||||
- Functions have zero or one or more than one arguments, argument type after the argument name and broke up by `,`.
|
||||
- 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 name and use type only.
|
||||
- If there is only one return value and you omitted the name, you don't need brackets for return values anymore.
|
||||
- If the function doesn't have return values, you can omit return part.
|
||||
- If the function has return values, you have to use `return` statement in some places in the body of function.
|
||||
- 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.
|
||||
|
||||
Let's see one practical example. (calculate maximum value)
|
||||
|
||||
@@ -223,13 +223,13 @@ Let's see one practical example. (calculate maximum value)
|
||||
fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // call function here
|
||||
}
|
||||
|
||||
In the above example, there are two arguments in function `max`, their type are both `int`, so the first type can be omitted, like `a, b int` instead of `a int, b int`. Same rules for more arguments. Notice here the `max` only have one return value, so we only write type of return value, this is a short form.
|
||||
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.
|
||||
|
||||
### Multi-value return
|
||||
|
||||
One thing that Go is better than C is that it supports multi-value return.
|
||||
One thing that Go is better at than C is that it supports multi-value returns.
|
||||
|
||||
We use above example here.
|
||||
We'll use the following example here.
|
||||
|
||||
package main
|
||||
import "fmt"
|
||||
@@ -249,7 +249,7 @@ We use above example here.
|
||||
fmt.Printf("%d * %d = %d\n", x, y, xTIMESy)
|
||||
}
|
||||
|
||||
Above example return two values without name, and you can name them also. If we named return values, we just use `return` to return values is fine because they initializes in the function automatically. Notice that if your functions are going to be used outside the package, which means your functions name start with capital letter, you'd better write complete statement for `return`; it makes your code more readable.
|
||||
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.
|
||||
|
||||
func SumAndProduct(A, B int) (add int, Multiplied int) {
|
||||
add = A+B
|
||||
@@ -259,11 +259,11 @@ Above example return two values without name, and you can name them also. If we
|
||||
|
||||
### Variable arguments
|
||||
|
||||
Go supports variable arguments, which means you can give uncertain number of argument to functions.
|
||||
Go supports variable arguments, which means you can give an uncertain numbers of argument to functions.
|
||||
|
||||
func myfunc(arg ...int) {}
|
||||
|
||||
`arg …int` tells Go this is the function that has variable arguments. Notice that these arguments are type `int`. In the body of function, the `arg` becomes a `slice` of `int`.
|
||||
`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`.
|
||||
|
||||
for _, n := range arg {
|
||||
fmt.Printf("And the number is: %d\n", n)
|
||||
@@ -271,9 +271,9 @@ Go supports variable arguments, which means you can give uncertain number of arg
|
||||
|
||||
### Pass by value and pointers
|
||||
|
||||
When we pass an argument to the function that was called, that function actually gets the copy of our variables, any change will not affect to the original variable.
|
||||
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.
|
||||
|
||||
Let's see one example to prove my words.
|
||||
Let's see one example to prove what i'm saying.
|
||||
|
||||
package main
|
||||
import "fmt"
|
||||
@@ -301,7 +301,7 @@ The reason is very simple: when we called `add1`, we gave a copy of `x` to it, n
|
||||
|
||||
Now you may ask how I can pass the real `x` to the function.
|
||||
|
||||
We need use pointers here. We know variables store in the memory, and they all have memory address, we change value of variable is to change the value in that variable's memory address. Therefore the function `add1` have to know the memory address of `x` in order to change its value. Here we pass `&x` to the function, and change argument's type to pointer type `*int`. Be aware that we pass a copy of pointer, not copy of value.
|
||||
We need use pointers here. We know variables are stored in memory and that they all have memory addresses. So, if we want to change the value of a variable, we must change the value at that variable's 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.
|
||||
|
||||
package main
|
||||
import "fmt"
|
||||
@@ -323,15 +323,15 @@ We need use pointers here. We know variables store in the memory, and they all h
|
||||
fmt.Println("x = ", x) // should print "x = 4"
|
||||
}
|
||||
|
||||
Now we can change value of `x` in the functions. Why we use pointers? What are the advantages?
|
||||
Now we can change the value of `x` in the functions. Why do we use pointers? What are the advantages?
|
||||
|
||||
- Use more functions to operate one variable.
|
||||
- Low cost by passing memory addresses (8 bytes), copy is not an efficient way in both time and space to pass variables.
|
||||
- `string`, `slice`, `map` are reference types, so they use pointers when pass to functions as default. (Attention: If you need to change length of `slice`, you have to pass pointers explicitly)
|
||||
- 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)
|
||||
|
||||
### defer
|
||||
|
||||
Go has a good design called `defer`, you can have many `defer` statements in one function; they will execute by reverse order when the program executes to the end of functions. Especially when the program open some resource files, these files have to be closed before the function return with errors. Let's see some examples.
|
||||
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.
|
||||
|
||||
func ReadWrite() bool {
|
||||
file.Open("file")
|
||||
@@ -350,7 +350,7 @@ Go has a good design called `defer`, you can have many `defer` statements in one
|
||||
return true
|
||||
}
|
||||
|
||||
We saw some code repeat several times, `defer` solves this problem very well. It doesn't only help you make clean code, and also make code more readable.
|
||||
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.
|
||||
|
||||
func ReadWrite() bool {
|
||||
file.Open("file")
|
||||
@@ -364,7 +364,7 @@ We saw some code repeat several times, `defer` solves this problem very well. It
|
||||
return true
|
||||
}
|
||||
|
||||
If there are more than one `defer`, they will execute by reverse order. The following example will print `4 3 2 1 0`.
|
||||
If there are more than one `defer`s, they will execute by reverse order. The following example will print `4 3 2 1 0`.
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
defer fmt.Printf("%d ", i)
|
||||
@@ -372,11 +372,11 @@ If there are more than one `defer`, they will execute by reverse order. The foll
|
||||
|
||||
### Functions as values and types
|
||||
|
||||
Functions are also variables in Go, we can use `type` to define them. Functions that have same signature can be seen as same type.
|
||||
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.
|
||||
|
||||
type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...])
|
||||
|
||||
What's the advantage of this feature? So that we can pass functions as values.
|
||||
What's the advantage of this feature? The answer is that it allows us to pass functions as values.
|
||||
|
||||
package main
|
||||
import "fmt"
|
||||
@@ -418,15 +418,15 @@ What's the advantage of this feature? So that we can pass functions as values.
|
||||
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 have more complex logic in our programs, and make code more flexible.
|
||||
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.
|
||||
|
||||
### Panic and Recover
|
||||
|
||||
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.
|
||||
|
||||
Panic is a built-in function to break normal flow of programs and get into panic status. When function `F` called `panic`, function `F` will not continue executing, but its `defer` functions are always executing. Then `F` goes back to its break point where causes panic status. The program will not end until all the 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.
|
||||
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.
|
||||
|
||||
Recover is a built-in function to recover `goroutine` from panic status, only call `recover` in `defer` functions is useful because normal functions will not be executed when the program is in the panic status. It catches `panic` value if the program is in the panic status, it gets `nil` if the program is not in panic status.
|
||||
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.
|
||||
|
||||
The following example shows how to use `panic`.
|
||||
|
||||
@@ -452,11 +452,11 @@ The following example shows how to check `panic`.
|
||||
|
||||
### `main` function and `init` function
|
||||
|
||||
Go has two retention 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 function are not able to have arguments or return values. Even though we can write many `init` function in one package, I strongly recommend to write only one `init` function for each package.
|
||||
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.
|
||||
|
||||
Go programs will call `init()` and `main()` automatically, so you don't need to call them by yourself. For every package, function `init` is optional, but `package main` has one and only one `main` function.
|
||||
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.
|
||||
|
||||
Programs initializes and executes from `main` package, if `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 imported packages, programs will initialize constants and variables in imported packages, then execute `init` function if it exists, and so on. After all the other packages were initialized, programs start initialize constants and variables in `main` package, then execute `init` function in package if it exists. The following figure shows the process.
|
||||
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.
|
||||
|
||||

|
||||
|
||||
@@ -474,7 +474,7 @@ Then we use functions in that package as follows.
|
||||
|
||||
fmt.Println("hello world")
|
||||
|
||||
`fmt` is from Go standard library, it locates in $GOROOT/pkg. Go uses two ways to support third-party packages.
|
||||
`fmt` is from Go standard library, it is located within $GOROOT/pkg. Go supports third-party packages in two ways.
|
||||
|
||||
1. Relative path
|
||||
import "./model" // load package in the same directory, I don't recommend this way.
|
||||
@@ -490,9 +490,9 @@ There are some special operators when we import packages, and beginners are alwa
|
||||
. "fmt"
|
||||
)
|
||||
|
||||
The dot operator means you can omit package name when you call functions in that package. Now `fmt.Printf("Hello world")` becomes to `Printf("Hello world")`.
|
||||
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 package that we imported when we call functions in that package.
|
||||
It changes the name of the package that we imported when we call functions that belong to that package.
|
||||
|
||||
import(
|
||||
f "fmt"
|
||||
@@ -500,14 +500,14 @@ There are some special operators when we import packages, and beginners are alwa
|
||||
|
||||
Now `fmt.Printf("Hello world")` becomes to `f.Printf("Hello world")`.
|
||||
3. `_` operator.
|
||||
This is the operator that hard to understand whitout someone explanning to you.
|
||||
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 import that package, and use `init` function in that package, and we are not sure if want to use functions in that package.
|
||||
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.
|
||||
|
||||
## Links
|
||||
|
||||
|
||||
Reference in New Issue
Block a user