Functions are organized, reusable blocks of code used to perform specified tasks. This paper introduces the related contents of functions in Go language.
Go language supports functions, anonymous functions and closures, and functions belong to "first-class citizens" in go language.
Function definition
func keyword is used for functions defined in Go language. The specific format is as follows:
func Function name(parameter)(Return value){ Function body }
Of which:
- Function name: composed of letters, numbers and underscores. However, the first letter of a function name cannot be a number. In the same package, the function name can not be duplicate (see the following for the concept of package).
- Parameters: parameters are composed of parameter variables and types of parameter variables. Multiple parameters are separated.
- Return value: the return value consists of the return value variable and its variable type. You can also write only the type of the return value. Multiple return values must be wrapped in () and separated by.
- Function body: a block of code that implements a specified function.
Let's first define a function that sums two numbers:
func intSum(x int, y int) int { return x + y } //Or add ret before the return type, and you can use ret directly in the code func sum(x int, y int)(ret int){ return x + y }
After writing the return value, you can directly use return
[the external chain picture transfer fails, and the source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-5rndZRuF-1627082528295)(go language development note refined version. assets/image-20210721161801466.png)]
The parameters and return values of the function are optional. For example, we can implement a function that requires neither parameters nor return values:
func sayHello() { fmt.Println("Hello Shahe") }
Function call
After defining the function, we can call the function by the function name (). For example, we call the two functions defined above. The code is as follows:
func main() { sayHello() ret := intSum(10, 20) fmt.Println(ret) }
Note that when you call a function with a return value, you can not receive its return value.
parameter
Type abbreviation
In the parameters of the function, if the types of adjacent variables are the same, the types can be omitted, for example:
func intSum(x, y int) int { return x + y }
In the above code, the intSum function has two parameters. The types of these two parameters are int. therefore, the type of x can be omitted, because there is a type description after y, and the x parameter is also of this type.
Variable parameters
Variable parameter means that the number of parameters of a function is not fixed. Variable parameters in Go language by adding To identify.
Note: variable parameters are usually used as the last parameter of the function.
for instance:
func intSum2(x ...int) int { fmt.Println(x) //x is a slice sum := 0 for _, v := range x { sum = sum + v } return sum }
Call the above function:
ret1 := intSum2() ret2 := intSum2(10) ret3 := intSum2(10, 20) ret4 := intSum2(10, 20, 30) fmt.Println(ret1, ret2, ret3, ret4) //0 10 30 60
When using fixed parameters, the following examples are used:
func intSum3(x int, y ...int) int { fmt.Println(x, y) sum := x for _, v := range y { sum = sum + v } return sum }
Call the above function:
ret5 := intSum3(100) ret6 := intSum3(100, 10) ret7 := intSum3(100, 10, 20) ret8 := intSum3(100, 10, 20, 30) fmt.Println(ret5, ret6, ret7, ret8) //100 110 130 160
In essence, the variable parameters of a function are implemented by slicing.
Return value
In the Go language, the return value is output through the return keyword.
Multiple return values
Functions in Go language support multiple return values. If a function has multiple return values, all return values must be wrapped with ().
for instance:
func calc(x, y int) (int, int) { sum := x + y sub := x - y return sum, sub }
Return value naming
When defining a function, you can name the return value, use these variables directly in the function body, and finally return it through the return keyword.
For example:
func calc(x, y int) (sum, sub int) { sum = x + y sub = x - y return }
Return value supplement
When the return value type of one of our functions is slice, nil can be regarded as a valid slice. There is no need to display and return a slice with length of 0.
func someFunc(x string) []int { if x == "" { return nil // There is no need to return [] int{} } ... }
Function advanced order
Variable scope
global variable
A global variable is a variable defined outside a function and is valid throughout the program's running cycle. Global variables can be accessed in functions.
package main import "fmt" //Define global variable num var num int64 = 10 func testGlobalVar() { fmt.Printf("num=%d\n", num) //The global variable num can be accessed in the function } func main() { testGlobalVar() //num=10 }
local variable
Local variables are divided into two types: variables defined in the function cannot be used outside the function. For example, the variable x defined in the testLocalVar function cannot be used in the main function of the following example code:
func testLocalVar() { //Define a function local variable x, which takes effect only within the function var x int64 = 100 fmt.Printf("x=%d\n", x) } func main() { testLocalVar() fmt.Println(x) // The variable x cannot be used at this time }
If the local variable and the global variable have the same name, the local variable is accessed first.
package main import "fmt" //Define global variable num var num int64 = 10 func testNum() { num := 100 fmt.Printf("num=%d\n", num) // Local variables are preferred in functions } func main() { testNum() // num=100 }
Next, let's look at the variables defined by the statement block. We usually use this method to define variables in if conditional judgment, for loop and switch statements.
func testLocalVar2(x, y int) { fmt.Println(x, y) //The parameters of the function also take effect only in this function if x > 0 { z := 100 //The variable z takes effect only in the if statement block fmt.Println(z) } //fmt.Println(z) / / variable Z cannot be used here }
In addition, the variables defined in the for loop statement we talked about earlier also take effect only in the for statement block:
func testLocalVar3() { for i := 0; i < 10; i++ { fmt.Println(i) //The variable i takes effect only in the current for statement block } //fmt.Println(i) / / variable i cannot be used here }
Function types and variables
Define function type
We can use the type keyword to define a function type. The specific format is as follows:
type calculation func(int, int) int
The above statement defines a calculation type, which is a function type. This function receives two parameters of type int and returns a return value of type int.
In short, all functions that meet this condition are of type calculation. For example, the following add and sub are of type calculation.
func add(x, y int) int { return x + y } func sub(x, y int) int { return x - y }
Both add and sub can be assigned to variables of type calculation.
var c calculation c = add
Function type variable
We can declare a variable of function type and assign a value to it:
func main() { var c calculation // Declare a variable of type calculation c c = add // Assign add to c fmt.Printf("type of c:%T\n", c) // type of c:main.calculation fmt.Println(c(1, 2)) // Call c like add f := add // Assign the function add to the variable f1 fmt.Printf("type of f:%T\n", f) // type of f:func(int, int) int fmt.Println(f(10, 20)) // Call f as you call add }
Higher order function
Higher order functions are divided into two parts: functions as parameters and functions as return values.
Function as parameter
Functions can be used as arguments:
func add(x, y int) int { return x + y } func calc(x, y int, op func(int, int) int) int { return op(x, y) } func main() { ret2 := calc(10, 20, add) fmt.Println(ret2) //30 }
Function as return value
Functions can also be used as return values:
func do(s string) (func(int, int) int, error) { switch s { case "+": return add, nil case "-": return sub, nil default: err := errors.New("Unrecognized operator") return nil, err } }
Anonymous functions and closures
Anonymous function
Of course, functions can also be used as return values, but in Go language, functions can no longer be defined inside functions as before, only anonymous functions can be defined. Anonymous functions are functions without function names. The definition format of anonymous functions is as follows:
func(parameter)(Return value){ Function body }
Anonymous functions cannot be called like ordinary functions because they do not have a function name. Therefore, anonymous functions need to be saved to a variable or used as immediate execution functions:
func main() { // Save anonymous functions to variables add := func(x, y int) { fmt.Println(x + y) } add(10, 20) // Calling anonymous functions through variables //Self executing function: anonymous function definition plus () is executed directly func(x, y int) { fmt.Println(x + y) }(10, 20) }
Anonymous functions are mostly used to implement callback functions and closures.
closure
A closure is an entity composed of a function and its associated reference environment. Simply put, closure = function + reference environment. Let's start with an example:
func adder() func(int) int { var x int return func(y int) int { x += y return x } } func main() { var f = adder() fmt.Println(f(10)) //10 fmt.Println(f(20)) //30 fmt.Println(f(30)) //60 f1 := adder() fmt.Println(f1(40)) //40 fmt.Println(f1(50)) //90 }
When the variable f is a function and it references the X variable in its external scope, f is a closure. The variable x is also valid throughout the life cycle of F. Advanced closure example 1:
func adder2(x int) func(int) int { return func(y int) int { x += y return x } } func main() { var f = adder2(10) fmt.Println(f(10)) //20 fmt.Println(f(20)) //40 fmt.Println(f(30)) //70 f1 := adder2(20) fmt.Println(f1(40)) //60 fmt.Println(f1(50)) //110 }
Advanced closure example 2:
func makeSuffixFunc(suffix string) func(string) string { return func(name string) string { if !strings.HasSuffix(name, suffix) { return name + suffix } return name } } func main() { jpgFunc := makeSuffixFunc(".jpg") txtFunc := makeSuffixFunc(".txt") fmt.Println(jpgFunc("test")) //test.jpg fmt.Println(txtFunc("test")) //test.txt }
Advanced closure example 3:
func calc(base int) (func(int) int, func(int) int) { add := func(i int) int { base += i return base } sub := func(i int) int { base -= i return base } return add, sub } func main() { f1, f2 := calc(10) fmt.Println(f1(1), f2(2)) //11 9 fmt.Println(f1(3), f2(4)) //12 8 fmt.Println(f1(5), f2(6)) //13 7 }
Closures are not complicated. Just remember that closures = functions + reference environments.
defer statement
The defer statement in the Go language delays the statements that follow it. When the function to which defer belongs is about to return, the delayed statements are executed in the reverse order defined by defer, that is, the statements first deferred are executed last, and the statements last deferred are executed first.
for instance:
func main() { fmt.Println("start") defer fmt.Println(1) defer fmt.Println(2) defer fmt.Println(3) fmt.Println("end") }
Output results:
start end 3 2 1
Due to the delayed calling of defer statement, defer statement can easily deal with the problem of resource release. For example, resource cleaning, file closing, unlocking and recording time.
defer execution timing
In the function of Go language, the return statement is not an atomic operation at the bottom. It is divided into two steps: assigning a return value and RET instruction. The defer statement is executed after the return value assignment operation and before the RET instruction is executed. See the following figure for details:
It is mainly used to open files, close them last, and close database connections last.
defer classic case
Read the following code and write the final print result.
func f1() int { x := 5 defer func() { x++ }() return x } func f2() (x int) { defer func() { x++ }() return 5 } func f3() (y int) { x := 5 defer func() { x++ }() return x } func f4() (x int) { defer func(x int) { x++ }(x) return 5 } func main() { fmt.Println(f1()) fmt.Println(f2()) fmt.Println(f3()) fmt.Println(f4()) }
defer interview questions
func calc(index string, a, b int) int { ret := a + b fmt.Println(index, a, b, ret) return ret } func main() { x := 1 y := 2 defer calc("AA", x, calc("A", x, y)) x = 10 defer calc("BB", x, calc("B", x, y)) y = 20 }
Q: what is the output of the above code? (Note: when defer registers a function to be delayed, all parameters of the function need to be determined)
Introduction to built-in functions
Built in function | introduce |
---|---|
close | It is mainly used to close the channel |
len | It is used to find the length, such as string, array, slice, map and channel |
new | It is used to allocate memory. It is mainly used to allocate value types, such as int and struct. The pointer is returned |
make | It is used to allocate memory. It is mainly used to allocate reference types, such as chan, map and slice |
append | Used to append elements to arrays and slice s |
panic and recover | Used for error handling |
panic/recover
At present (Go1.12), there is no exception mechanism in the Go language, but the panic/recover mode is used to handle errors. Panic can be raised anywhere, but recover is only valid in the function called by defer. Let's start with an example:
func funcA() { fmt.Println("func A") } func funcB() { panic("panic in B") } func funcC() { fmt.Println("func C") } func main() { funcA() funcB() funcC() }
Output:
func A panic: panic in B goroutine 1 [running]: main.funcB(...) .../code/func/main.go:12 main.main() .../code/func/main.go:20 +0x98
During program running, panic was thrown in funcB, resulting in program crash and abnormal exit. At this time, we can recover the program through recover and continue to execute it later.
func funcA() { fmt.Println("func A") } func funcB() { defer func() { err := recover() //If a panic error occurs in the program, it can be recovered through recover if err != nil { fmt.Println("recover in B") } }() panic("panic in B") } func funcC() { fmt.Println("func C") } func main() { funcA() funcB() funcC() }
be careful:
- recover() must be used with defer.
- The defer must be defined before the statement that may cause panic.
Exercises
- Cent gold
/* You have 50 gold coins to be distributed to the following people: Matthew,Sarah,Augustus,Heidi,Emilie,Peter,Giana,Adriano,Aaron,Elizabeth. The allocation rules are as follows: a. Each 'e' or 'e' in the name is divided into one gold coin b. Two gold coins for each 'I' or 'I' in the name c. Each 'o' or 'o' in the name is divided into three gold coins d: Each 'U' or 'U' in the name is divided into 4 gold coins Write a program to calculate how many gold coins each user gets and how many gold coins are left in the end? The program structure is as follows. Please implement the 'dispatchCoin' function */ var ( coins = 50 users = []string{ "Matthew", "Sarah", "Augustus", "Heidi", "Emilie", "Peter", "Giana", "Adriano", "Aaron", "Elizabeth", } distribution = make(map[string]int, len(users)) ) func main() { left := dispatchCoin() fmt.Println("be left over:", left) }