Basic functions of Go language

Posted by rabab.orthalis on Fri, 14 Jan 2022 23:30:15 +0100

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 functionintroduce
closeIt is mainly used to close the channel
lenIt is used to find the length, such as string, array, slice, map and channel
newIt is used to allocate memory. It is mainly used to allocate value types, such as int and struct. The pointer is returned
makeIt is used to allocate memory. It is mainly used to allocate reference types, such as chan, map and slice
appendUsed to append elements to arrays and slice s
panic and recoverUsed 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:

  1. recover() must be used with defer.
  2. The defer must be defined before the statement that may cause panic.

Exercises

  1. 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)
}

Topics: Go